Rewritten HttpHeader::fill()

pull/2/head
Tatsuhiro Tsujikawa 2011-11-03 00:31:27 +09:00
parent 14eee59ac4
commit e8d4deecad
6 changed files with 171 additions and 72 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,9 @@
#include "HttpHeader.h" #include "HttpHeader.h"
#include <sstream>
#include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/HelperMacros.h>
#include "Range.h" #include "Range.h"
#include "DlAbortEx.h"
namespace aria2 { namespace aria2 {
@ -71,6 +70,56 @@ void HttpHeaderTest::testGetRange()
CPPUNIT_ASSERT_EQUAL((off_t)0, range->getEndByte()); CPPUNIT_ASSERT_EQUAL((off_t)0, range->getEndByte());
CPPUNIT_ASSERT_EQUAL((uint64_t)0, range->getEntityLength()); 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() void HttpHeaderTest::testGet()
@ -104,16 +153,16 @@ void HttpHeaderTest::testClearField()
void HttpHeaderTest::testFill() void HttpHeaderTest::testFill()
{ {
std::stringstream ss; std::string s =
ss << "Host: aria2.sourceforge.net\r\n" "Host: aria2.sourceforge.net\r\n"
<< "Connection: close \r\n" // trailing white space "Connection: close \r\n" // trailing white space
<< "Multi-Line: text1\r\n" "Multi-Line: text1\r\n"
<< " text2\r\n" " text2\r\n"
<< " text3\r\n" " text3\r\n"
<< "Duplicate: foo\r\n" "Duplicate: foo\r\n"
<< "Duplicate: bar\r\n"; "Duplicate: bar\r\n";
HttpHeader h; HttpHeader h;
h.fill(ss); h.fill(s.begin(), s.end());
CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.net"), CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.net"),
h.getFirst("Host")); h.getFirst("Host"));
CPPUNIT_ASSERT_EQUAL(std::string("close"), CPPUNIT_ASSERT_EQUAL(std::string("close"),