mirror of https://github.com/aria2/aria2
Rewritten HttpHeader::fill()
parent
14eee59ac4
commit
e8d4deecad
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Reference in New Issue