ソースを参照

Rewritten HttpHeader::fill()

Tatsuhiro Tsujikawa 14 年 前
コミット
e8d4deecad

+ 81 - 46
src/HttpHeader.cc

@@ -33,9 +33,6 @@
  */
 /* copyright --> */
 #include "HttpHeader.h"
-
-#include <istream>
-
 #include "Range.h"
 #include "util.h"
 #include "A2STR.h"
@@ -152,38 +149,37 @@ RangeHandle HttpHeader::getRange() const
       }
     }
   }
-  std::string byteRangeSpec;
-  {
-    // we expect that rangeStr looks like 'bytes 100-199/100'
-    // but some server returns '100-199/100', omitting bytes-unit sepcifier
-    // 'bytes'.
-    std::pair<std::string, std::string> splist;
-    util::divide(splist, rangeStr, ' ');
-    if(splist.second.empty()) {
-      // we assume bytes-unit specifier omitted.
-      byteRangeSpec = splist.first;
-    } else {
-      byteRangeSpec = splist.second;
+  // we expect that rangeStr looks like 'bytes 100-199/100'
+  // but some server returns '100-199/100', omitting bytes-unit sepcifier
+  // 'bytes'.
+  std::string::const_iterator byteRangeSpec =
+    std::find(rangeStr.begin(), rangeStr.end(), ' ');
+  if(byteRangeSpec == rangeStr.end()) {
+    // we assume bytes-unit specifier omitted.
+    byteRangeSpec = rangeStr.begin();
+  } else {
+    while(byteRangeSpec != rangeStr.end() &&
+          (*byteRangeSpec == ' ' || *byteRangeSpec == '\t')) {
+      ++byteRangeSpec;
     }
   }
-  std::pair<std::string, std::string> byteRangeSpecPair;
-  util::divide(byteRangeSpecPair, byteRangeSpec, '/');
-
-  if(util::strip(byteRangeSpecPair.first) == "*" ||
-     util::strip(byteRangeSpecPair.second) == "*") {
+  std::string::const_iterator slash =
+    std::find(byteRangeSpec, rangeStr.end(), '/');
+  if(slash == rangeStr.end() || slash+1 == rangeStr.end() ||
+     (byteRangeSpec+1 == slash && *byteRangeSpec == '*') ||
+     (slash+2 == rangeStr.end() && *(slash+1) == '*')) {
     // If byte-range-resp-spec or instance-length is "*", we returns
     // empty Range. The former is usually sent with 416 (Request range
     // not satisfiable) status.
     return SharedHandle<Range>(new Range());
   }
-
-  std::pair<std::string, std::string> byteRangeRespSpecPair;
-  util::divide(byteRangeRespSpecPair, byteRangeSpecPair.first, '-');
-
-  off_t startByte = util::parseLLInt(byteRangeRespSpecPair.first);
-  off_t endByte = util::parseLLInt(byteRangeRespSpecPair.second);
-  uint64_t entityLength = util::parseULLInt(byteRangeSpecPair.second);
-
+  std::string::const_iterator minus = std::find(byteRangeSpec, slash, '-');
+  if(minus == slash) {
+    return SharedHandle<Range>(new Range());
+  }
+  off_t startByte = util::parseLLInt(std::string(byteRangeSpec, minus));
+  off_t endByte = util::parseLLInt(std::string(minus+1, slash));
+  uint64_t entityLength = util::parseULLInt(std::string(slash+1, rangeStr.end()));
   return SharedHandle<Range>(new Range(startByte, endByte, entityLength));
 }
 
@@ -202,28 +198,67 @@ void HttpHeader::setRequestPath(const std::string& requestPath)
   requestPath_ = requestPath;
 }
 
-void HttpHeader::fill(std::istream& in)
+namespace {
+template<typename InputIterator>
+std::pair<InputIterator, InputIterator> stripIter2
+(InputIterator first, InputIterator last,
+ const std::string& chars = "\r\n \t")
 {
-  std::string line;
-  std::getline(in, line);
-  while(in) {
-    line = util::strip(line);
-    if(line.empty()) {
-      std::getline(in, line);
-    } else {
-      std::pair<std::string, std::string> hp;
-      util::divide(hp, line, ':');
-      while(std::getline(in, line)) {
-        if(!line.empty() && (line[0] == ' ' || line[0] == '\t')) {
-          line = util::strip(line);
-          hp.second += " ";
-          hp.second += line;
-        } else {
-          break;
+  for(; first != last &&
+        std::find(chars.begin(), chars.end(), *first) != chars.end(); ++first);
+  if(first == last) {
+    return std::make_pair(first, last);
+  }
+  InputIterator left = last-1;
+  for(; left != first &&
+        std::find(chars.begin(), chars.end(), *left) != chars.end(); --left);
+  return std::make_pair(first, left+1);
+}
+} // namespace
+
+void HttpHeader::fill
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+  std::string name;
+  std::string value;
+  while(first != last) {
+    std::string::const_iterator j = first;
+    while(j != last && *j != '\r' && *j != '\n') {
+      ++j;
+    }
+    if(first != j) {
+      std::string::const_iterator sep = std::find(first, j, ':');
+      if(sep == j) {
+        // multiline header?
+        if(*first == ' ' || *first == '\t') {
+          std::pair<std::string::const_iterator,
+                    std::string::const_iterator> p = stripIter2(first, j);
+          if(!name.empty() && p.first != p.second) {
+            if(!value.empty()) {
+              value += ' ';
+            }
+            value.append(p.first, p.second);
+          }
+        }
+      } else {
+        if(!name.empty()) {
+          put(name, value);
         }
+        std::pair<std::string::const_iterator,
+                  std::string::const_iterator> p = stripIter2(first, sep);
+        name.assign(p.first, p.second);
+        p = stripIter2(sep+1, j);
+        value.assign(p.first, p.second);
       }
-      put(hp.first, hp.second);
     }
+    while(j != last && (*j == '\r' || *j == '\n')) {
+      ++j;
+    }
+    first = j;
+  }
+  if(!name.empty()) {
+    put(name, value);
   }
 }
 

+ 3 - 2
src/HttpHeader.h

@@ -40,7 +40,6 @@
 #include <map>
 #include <vector>
 #include <string>
-#include <iosfwd>
 
 #include "SharedHandle.h"
 
@@ -110,7 +109,9 @@ public:
 
   void setRequestPath(const std::string& requestPath);
 
-  void fill(std::istream& in);
+  void fill
+  (std::string::const_iterator first,
+   std::string::const_iterator last);
 
   // Clears table_. responseStatus_ and version_ are unchanged.
   void clearField();

+ 11 - 6
src/HttpHeaderProcessor.cc

@@ -34,7 +34,6 @@
 /* copyright --> */
 #include "HttpHeaderProcessor.h"
 
-#include <sstream>
 #include <vector>
 
 #include "HttpHeader.h"
@@ -60,7 +59,7 @@ HttpHeaderProcessor::~HttpHeaderProcessor() {}
 void HttpHeaderProcessor::update(const unsigned char* data, size_t length)
 {
   checkHeaderLimit(length);
-  buf_ += std::string(&data[0], &data[length]);
+  buf_.append(&data[0], &data[length]);
 }
 
 void HttpHeaderProcessor::update(const std::string& data)
@@ -119,10 +118,13 @@ SharedHandle<HttpHeader> HttpHeaderProcessor::getHttpResponseHeader()
   HttpHeaderHandle httpHeader(new HttpHeader());
   httpHeader->setVersion(buf_.substr(0, 8));
   httpHeader->setStatusCode(statusCode);
-  std::istringstream strm(buf_);
   // TODO 1st line(HTTP/1.1 200...) is also send to HttpHeader, but it should
   // not.
-  httpHeader->fill(strm);
+  if((delimpos = buf_.find("\r\n\r\n")) == std::string::npos &&
+     (delimpos = buf_.find("\n\n")) == std::string::npos) {
+    delimpos = buf_.size();
+  }
+  httpHeader->fill(buf_.begin(), buf_.begin()+delimpos);
   return httpHeader;
 }
 
@@ -147,8 +149,11 @@ SharedHandle<HttpHeader> HttpHeaderProcessor::getHttpRequestHeader()
   httpHeader->setMethod(firstLine[0]);
   httpHeader->setRequestPath(firstLine[1]);
   httpHeader->setVersion(firstLine[2]);
-  std::istringstream strm(buf_.substr(delimpos));
-  httpHeader->fill(strm);
+  if((delimpos = buf_.find("\r\n\r\n")) == std::string::npos &&
+     (delimpos = buf_.find("\n\n")) == std::string::npos) {
+    delimpos = buf_.size();
+  }
+  httpHeader->fill(buf_.begin(), buf_.begin()+delimpos);
   return httpHeader;
 }
 

+ 3 - 1
src/HttpHeaderProcessor.h

@@ -36,10 +36,12 @@
 #define D_HTTP_HEADER_PROCESSOR_H
 
 #include "common.h"
-#include "SharedHandle.h"
+
 #include <utility>
 #include <string>
 
+#include "SharedHandle.h"
+
 namespace aria2 {
 
 class HttpHeader;

+ 11 - 4
test/HttpHeaderProcessorTest.cc

@@ -1,9 +1,12 @@
 #include "HttpHeaderProcessor.h"
+
+#include <iostream>
+
+#include <cppunit/extensions/HelperMacros.h>
+
 #include "HttpHeader.h"
 #include "DlRetryEx.h"
 #include "DlAbortEx.h"
-#include <iostream>
-#include <cppunit/extensions/HelperMacros.h>
 
 namespace aria2 {
 
@@ -102,7 +105,8 @@ void HttpHeaderProcessorTest::testGetHttpResponseHeader()
     "Content-Length: 9187\r\n"
     "Connection: close\r\n"
     "Content-Type: text/html; charset=UTF-8\r\n"
-    "\r\n";
+    "\r\n"
+    "Entity: body";
 
   proc.update(hd);
 
@@ -116,6 +120,7 @@ void HttpHeaderProcessorTest::testGetHttpResponseHeader()
   CPPUNIT_ASSERT_EQUAL((uint64_t)9187ULL, header->getFirstAsULLInt("Content-Length"));
   CPPUNIT_ASSERT_EQUAL(std::string("text/html; charset=UTF-8"),
                        header->getFirst("Content-Type"));
+  CPPUNIT_ASSERT(!header->defined("entity"));
 }
 
 void HttpHeaderProcessorTest::testGetHttpResponseHeader_empty()
@@ -208,7 +213,8 @@ void HttpHeaderProcessorTest::testGetHttpRequestHeader()
   std::string request = "GET /index.html HTTP/1.1\r\n"
     "Host: host\r\n"
     "Connection: close\r\n"
-    "\r\n";
+    "\r\n"
+    "Entity: body";
 
   proc.update(request);
 
@@ -218,6 +224,7 @@ void HttpHeaderProcessorTest::testGetHttpRequestHeader()
   CPPUNIT_ASSERT_EQUAL(std::string("/index.html"),httpHeader->getRequestPath());
   CPPUNIT_ASSERT_EQUAL(std::string("HTTP/1.1"), httpHeader->getVersion());
   CPPUNIT_ASSERT_EQUAL(std::string("close"),httpHeader->getFirst("Connection"));
+  CPPUNIT_ASSERT(!httpHeader->defined("entity"));
 }
 
 } // namespace aria2

+ 60 - 11
test/HttpHeaderTest.cc

@@ -1,10 +1,9 @@
 #include "HttpHeader.h"
 
-#include <sstream>
-
 #include <cppunit/extensions/HelperMacros.h>
 
 #include "Range.h"
+#include "DlAbortEx.h"
 
 namespace aria2 {
 
@@ -71,6 +70,56 @@ void HttpHeaderTest::testGetRange()
     CPPUNIT_ASSERT_EQUAL((off_t)0, range->getEndByte());
     CPPUNIT_ASSERT_EQUAL((uint64_t)0, range->getEntityLength());
   }
+  {
+    HttpHeader httpHeader;
+    httpHeader.put("Content-Range", "bytes */*");
+
+    SharedHandle<Range> range = httpHeader.getRange();
+
+    CPPUNIT_ASSERT_EQUAL((off_t)0, range->getStartByte());
+    CPPUNIT_ASSERT_EQUAL((off_t)0, range->getEndByte());
+    CPPUNIT_ASSERT_EQUAL((uint64_t)0, range->getEntityLength());
+  }
+  {
+    HttpHeader httpHeader;
+    httpHeader.put("Content-Range", "bytes 0");
+
+    SharedHandle<Range> range = httpHeader.getRange();
+
+    CPPUNIT_ASSERT_EQUAL((off_t)0, range->getStartByte());
+    CPPUNIT_ASSERT_EQUAL((off_t)0, range->getEndByte());
+    CPPUNIT_ASSERT_EQUAL((uint64_t)0, range->getEntityLength());
+  }
+  {
+    HttpHeader httpHeader;
+    httpHeader.put("Content-Range", "bytes 0/");
+
+    SharedHandle<Range> range = httpHeader.getRange();
+
+    CPPUNIT_ASSERT_EQUAL((off_t)0, range->getStartByte());
+    CPPUNIT_ASSERT_EQUAL((off_t)0, range->getEndByte());
+    CPPUNIT_ASSERT_EQUAL((uint64_t)0, range->getEntityLength());
+  }
+  {
+    HttpHeader httpHeader;
+    httpHeader.put("Content-Range", "bytes 0-/3");
+    try {
+      httpHeader.getRange();
+      CPPUNIT_FAIL("Exception must be thrown");
+    } catch(const DlAbortEx& e) {
+      // success
+    }
+  }
+  {
+    HttpHeader httpHeader;
+    httpHeader.put("Content-Range", "bytes -0/3");
+    try {
+      httpHeader.getRange();
+      CPPUNIT_FAIL("Exception must be thrown");
+    } catch(const DlAbortEx& e) {
+      // success
+    }
+  }
 }
 
 void HttpHeaderTest::testGet()
@@ -104,16 +153,16 @@ void HttpHeaderTest::testClearField()
 
 void HttpHeaderTest::testFill()
 {
-  std::stringstream ss;
-  ss << "Host: aria2.sourceforge.net\r\n"
-     << "Connection: close \r\n" // trailing white space
-     << "Multi-Line: text1\r\n"
-     << "  text2\r\n"
-     << "  text3\r\n"
-     << "Duplicate: foo\r\n"
-     << "Duplicate: bar\r\n";
+  std::string s =
+    "Host: aria2.sourceforge.net\r\n"
+    "Connection: close \r\n" // trailing white space
+    "Multi-Line: text1\r\n"
+    "  text2\r\n"
+    "  text3\r\n"
+    "Duplicate: foo\r\n"
+    "Duplicate: bar\r\n";
   HttpHeader h;
-  h.fill(ss);
+  h.fill(s.begin(), s.end());
   CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.net"),
                        h.getFirst("Host"));
   CPPUNIT_ASSERT_EQUAL(std::string("close"),