mirror of https://github.com/aria2/aria2
Rewritten ChunkedDecodingStreamFilter
parent
f3a5aa3bef
commit
9ba65aea1d
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* aria2 - The high speed download utility
|
* aria2 - The high speed download utility
|
||||||
*
|
*
|
||||||
* Copyright (C) 2010 Tatsuhiro Tsujikawa
|
* Copyright (C) 2012 Tatsuhiro Tsujikawa
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -46,174 +46,164 @@ namespace aria2 {
|
||||||
const std::string ChunkedDecodingStreamFilter::NAME
|
const std::string ChunkedDecodingStreamFilter::NAME
|
||||||
("ChunkedDecodingStreamFilter");
|
("ChunkedDecodingStreamFilter");
|
||||||
|
|
||||||
size_t ChunkedDecodingStreamFilter::MAX_BUF_SIZE = 1024*1024;
|
namespace {
|
||||||
|
enum {
|
||||||
ChunkedDecodingStreamFilter::ReadChunkSizeStateHandler*
|
PREV_CHUNK_SIZE,
|
||||||
ChunkedDecodingStreamFilter::readChunkSizeStateHandler_ =
|
CHUNK_SIZE,
|
||||||
new ChunkedDecodingStreamFilter::ReadChunkSizeStateHandler();
|
CHUNK_EXTENSION,
|
||||||
|
PREV_CHUNK_SIZE_LF,
|
||||||
ChunkedDecodingStreamFilter::ReadTrailerStateHandler*
|
CHUNK,
|
||||||
ChunkedDecodingStreamFilter::readTrailerStateHandler_ =
|
PREV_CHUNK_CR,
|
||||||
new ChunkedDecodingStreamFilter::ReadTrailerStateHandler();
|
PREV_CHUNK_LF,
|
||||||
|
PREV_TRAILER,
|
||||||
ChunkedDecodingStreamFilter::ReadDataStateHandler*
|
TRAILER,
|
||||||
ChunkedDecodingStreamFilter::readDataStateHandler_ =
|
PREV_TRAILER_LF,
|
||||||
new ChunkedDecodingStreamFilter::ReadDataStateHandler();
|
PREV_END_CR,
|
||||||
|
PREV_END_LF,
|
||||||
ChunkedDecodingStreamFilter::ReadDataEndStateHandler*
|
CHUNKS_COMPLETE
|
||||||
ChunkedDecodingStreamFilter::readDataEndStateHandler_ =
|
};
|
||||||
new ChunkedDecodingStreamFilter::ReadDataEndStateHandler();
|
} // namespace
|
||||||
|
|
||||||
ChunkedDecodingStreamFilter::StreamEndStatehandler*
|
|
||||||
ChunkedDecodingStreamFilter::streamEndStateHandler_ =
|
|
||||||
new ChunkedDecodingStreamFilter::StreamEndStatehandler();
|
|
||||||
|
|
||||||
ChunkedDecodingStreamFilter::ChunkedDecodingStreamFilter
|
ChunkedDecodingStreamFilter::ChunkedDecodingStreamFilter
|
||||||
(const SharedHandle<StreamFilter>& delegate):
|
(const SharedHandle<StreamFilter>& delegate):
|
||||||
StreamFilter(delegate),
|
StreamFilter(delegate),
|
||||||
state_(readChunkSizeStateHandler_),
|
state_(PREV_CHUNK_SIZE),
|
||||||
chunkSize_(0),
|
chunkSize_(0),
|
||||||
|
chunkRemaining_(0),
|
||||||
bytesProcessed_(0) {}
|
bytesProcessed_(0) {}
|
||||||
|
|
||||||
ChunkedDecodingStreamFilter::~ChunkedDecodingStreamFilter() {}
|
ChunkedDecodingStreamFilter::~ChunkedDecodingStreamFilter() {}
|
||||||
|
|
||||||
void ChunkedDecodingStreamFilter::init() {}
|
void ChunkedDecodingStreamFilter::init() {}
|
||||||
|
|
||||||
bool ChunkedDecodingStreamFilter::readChunkSize
|
|
||||||
(size_t& inbufOffset, const unsigned char* inbuf, size_t inlen)
|
|
||||||
{
|
|
||||||
size_t pbufSize = buf_.size();
|
|
||||||
buf_.append(&inbuf[inbufOffset], &inbuf[inlen]);
|
|
||||||
std::string::size_type crlfPos = buf_.find(A2STR::CRLF);
|
|
||||||
if(crlfPos == std::string::npos) {
|
|
||||||
if(buf_.size() > MAX_BUF_SIZE) {
|
|
||||||
throw DL_ABORT_EX("Could not find chunk size before buffer got full.");
|
|
||||||
}
|
|
||||||
inbufOffset = inlen;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::string::size_type extPos = buf_.find(A2STR::SEMICOLON_C);
|
|
||||||
if(extPos == std::string::npos || crlfPos < extPos) {
|
|
||||||
extPos = crlfPos;
|
|
||||||
}
|
|
||||||
chunkSize_ = util::parseLLInt
|
|
||||||
(std::string(buf_.begin(), buf_.begin()+extPos), 16);
|
|
||||||
if(chunkSize_ < 0) {
|
|
||||||
throw DL_ABORT_EX("Chunk size must be positive");
|
|
||||||
}
|
|
||||||
assert(crlfPos+2 > pbufSize);
|
|
||||||
inbufOffset += crlfPos+2-pbufSize;
|
|
||||||
buf_.clear();
|
|
||||||
if(chunkSize_ == 0) {
|
|
||||||
state_ = readTrailerStateHandler_;
|
|
||||||
} else {
|
|
||||||
state_ = readDataStateHandler_;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChunkedDecodingStreamFilter::readTrailer
|
|
||||||
(size_t& inbufOffset, const unsigned char* inbuf, size_t inlen)
|
|
||||||
{
|
|
||||||
size_t pbufSize = buf_.size();
|
|
||||||
buf_.append(&inbuf[inbufOffset], &inbuf[inlen]);
|
|
||||||
std::string::size_type crlfcrlfPos = buf_.find("\r\n\r\n");
|
|
||||||
if(crlfcrlfPos != std::string::npos) {
|
|
||||||
// TODO crlfcrlfPos == 0 case?
|
|
||||||
inbufOffset += crlfcrlfPos+4-pbufSize;
|
|
||||||
inbufOffset = inlen;
|
|
||||||
buf_.clear();
|
|
||||||
state_ = streamEndStateHandler_;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
std::string::size_type crlfPos = buf_.find(A2STR::CRLF);
|
|
||||||
if(crlfPos == std::string::npos) {
|
|
||||||
if(buf_.size() > MAX_BUF_SIZE) {
|
|
||||||
throw DL_ABORT_EX
|
|
||||||
("Could not find end of stream before buffer got full.");
|
|
||||||
}
|
|
||||||
inbufOffset = inlen;
|
|
||||||
return false;
|
|
||||||
} else if(crlfPos == 0) {
|
|
||||||
inbufOffset += crlfPos+2-pbufSize;
|
|
||||||
buf_.clear();
|
|
||||||
state_ = streamEndStateHandler_;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if(buf_.size() > MAX_BUF_SIZE) {
|
|
||||||
throw DL_ABORT_EX
|
|
||||||
("Could not find end of stream before buffer got full.");
|
|
||||||
}
|
|
||||||
inbufOffset = inlen;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChunkedDecodingStreamFilter::readData
|
|
||||||
(ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment)
|
|
||||||
{
|
|
||||||
off_t readlen =
|
|
||||||
std::min(chunkSize_, static_cast<off_t>(inlen-inbufOffset));
|
|
||||||
outlen += getDelegate()->transform(out, segment, inbuf+inbufOffset, readlen);
|
|
||||||
chunkSize_ -= readlen;
|
|
||||||
inbufOffset += readlen;
|
|
||||||
if(chunkSize_ == 0) {
|
|
||||||
state_ = readDataEndStateHandler_;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChunkedDecodingStreamFilter::readDataEnd
|
|
||||||
(size_t& inbufOffset, const unsigned char* inbuf, size_t inlen)
|
|
||||||
{
|
|
||||||
size_t pbufSize = buf_.size();
|
|
||||||
buf_.append(&inbuf[inbufOffset], &inbuf[inlen]);
|
|
||||||
if(buf_.size() >= 2) {
|
|
||||||
if(buf_[0] == '\r' && buf_[1] == '\n') {
|
|
||||||
inbufOffset += 2-pbufSize;
|
|
||||||
buf_.clear();
|
|
||||||
state_ = readChunkSizeStateHandler_;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw DL_ABORT_EX("No CRLF at the end of chunk.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inbufOffset = inlen;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t ChunkedDecodingStreamFilter::transform
|
ssize_t ChunkedDecodingStreamFilter::transform
|
||||||
(const SharedHandle<BinaryStream>& out,
|
(const SharedHandle<BinaryStream>& out,
|
||||||
const SharedHandle<Segment>& segment,
|
const SharedHandle<Segment>& segment,
|
||||||
const unsigned char* inbuf, size_t inlen)
|
const unsigned char* inbuf, size_t inlen)
|
||||||
{
|
{
|
||||||
size_t inbufOffset = 0;
|
|
||||||
ssize_t outlen = 0;
|
ssize_t outlen = 0;
|
||||||
while(inbufOffset < inlen) {
|
size_t i;
|
||||||
ssize_t olen = 0;
|
bytesProcessed_ = 0;
|
||||||
bool r = state_->execute
|
for(i = 0; i < inlen; ++i) {
|
||||||
(this, olen, inbufOffset, inbuf, inlen, out, segment);
|
unsigned char c = inbuf[i];
|
||||||
outlen += olen;
|
switch(state_) {
|
||||||
if(!r) {
|
case PREV_CHUNK_SIZE:
|
||||||
|
if(util::isHexDigit(c)) {
|
||||||
|
chunkSize_ = util::hexCharToUInt(c);
|
||||||
|
state_ = CHUNK_SIZE;
|
||||||
|
} else {
|
||||||
|
throw DL_ABORT_EX("Bad chunk size: not hex string");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHUNK_SIZE:
|
||||||
|
if(util::isHexDigit(c)) {
|
||||||
|
if(chunkSize_ & 0x7800000000000000LL) {
|
||||||
|
throw DL_ABORT_EX("Too big chunk size");
|
||||||
|
}
|
||||||
|
chunkSize_ <<= 4;
|
||||||
|
chunkSize_ += util::hexCharToUInt(c);
|
||||||
|
} else if(c == ';') {
|
||||||
|
state_ = CHUNK_EXTENSION;
|
||||||
|
} else if(c == '\r') {
|
||||||
|
state_ = PREV_CHUNK_SIZE_LF;
|
||||||
|
} else {
|
||||||
|
throw DL_ABORT_EX("Bad chunk size: not hex string");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHUNK_EXTENSION:
|
||||||
|
if(c == '\r') {
|
||||||
|
state_ = PREV_CHUNK_SIZE_LF;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREV_CHUNK_SIZE_LF:
|
||||||
|
if(c == '\n') {
|
||||||
|
chunkRemaining_ = chunkSize_;
|
||||||
|
if(chunkSize_ == 0) {
|
||||||
|
state_ = PREV_TRAILER;
|
||||||
|
} else {
|
||||||
|
state_ = CHUNK;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw DL_ABORT_EX("Bad chunk encoding: "
|
||||||
|
"missing LF at the end of chunk size");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHUNK: {
|
||||||
|
int64_t readlen = std::min(chunkRemaining_,
|
||||||
|
static_cast<int64_t>(inlen-i));
|
||||||
|
outlen += getDelegate()->transform(out, segment, inbuf+i, readlen);
|
||||||
|
chunkRemaining_ -= readlen;
|
||||||
|
i += readlen-1;
|
||||||
|
if(chunkRemaining_ == 0) {
|
||||||
|
state_ = PREV_CHUNK_CR;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PREV_CHUNK_CR:
|
||||||
|
if(c == '\r') {
|
||||||
|
state_ = PREV_CHUNK_LF;
|
||||||
|
} else {
|
||||||
|
throw DL_ABORT_EX("Bad chunk encoding: "
|
||||||
|
"missing CR at the end of chunk");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREV_CHUNK_LF:
|
||||||
|
if(c == '\n') {
|
||||||
|
if(chunkSize_ == 0) {
|
||||||
|
state_ = PREV_TRAILER;
|
||||||
|
} else {
|
||||||
|
chunkSize_ = chunkRemaining_ = 0;
|
||||||
|
state_ = PREV_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw DL_ABORT_EX("Bad chunk encoding: "
|
||||||
|
"missing LF at the end of chunk");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREV_TRAILER:
|
||||||
|
if(c == '\r') {
|
||||||
|
// No trailer
|
||||||
|
state_ = PREV_END_LF;
|
||||||
|
} else {
|
||||||
|
state_= TRAILER;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TRAILER:
|
||||||
|
if(c == '\r') {
|
||||||
|
state_ = PREV_TRAILER_LF;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREV_TRAILER_LF:
|
||||||
|
if(c == '\n') {
|
||||||
|
state_ = PREV_TRAILER;
|
||||||
|
} else {
|
||||||
|
throw DL_ABORT_EX("Bad chunk encoding: "
|
||||||
|
"missing LF at the end of trailer");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREV_END_LF:
|
||||||
|
if(c == '\n') {
|
||||||
|
state_ = CHUNKS_COMPLETE;
|
||||||
|
} else {
|
||||||
|
throw DL_ABORT_EX("Bad chunk encoding: "
|
||||||
|
"missing LF at the end of chunks");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHUNKS_COMPLETE:
|
||||||
|
goto fin;
|
||||||
|
default:
|
||||||
|
// unreachable
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bytesProcessed_ = inbufOffset;
|
fin:
|
||||||
|
bytesProcessed_ += i;
|
||||||
return outlen;
|
return outlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChunkedDecodingStreamFilter::finished()
|
bool ChunkedDecodingStreamFilter::finished()
|
||||||
{
|
{
|
||||||
return state_ == streamEndStateHandler_ && getDelegate()->finished();
|
return state_ == CHUNKS_COMPLETE && getDelegate()->finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkedDecodingStreamFilter::release() {}
|
void ChunkedDecodingStreamFilter::release() {}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* aria2 - The high speed download utility
|
* aria2 - The high speed download utility
|
||||||
*
|
*
|
||||||
* Copyright (C) 2010 Tatsuhiro Tsujikawa
|
* Copyright (C) 2012 Tatsuhiro Tsujikawa
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -41,127 +41,10 @@ namespace aria2 {
|
||||||
|
|
||||||
class ChunkedDecodingStreamFilter : public StreamFilter {
|
class ChunkedDecodingStreamFilter : public StreamFilter {
|
||||||
private:
|
private:
|
||||||
class StateHandler {
|
int state_;
|
||||||
public:
|
int64_t chunkSize_;
|
||||||
virtual ~StateHandler() {}
|
int64_t chunkRemaining_;
|
||||||
|
|
||||||
virtual bool execute
|
|
||||||
(ChunkedDecodingStreamFilter* filter,
|
|
||||||
ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
StateHandler* state_;
|
|
||||||
|
|
||||||
std::string buf_;
|
|
||||||
|
|
||||||
off_t chunkSize_;
|
|
||||||
|
|
||||||
size_t bytesProcessed_;
|
size_t bytesProcessed_;
|
||||||
|
|
||||||
static size_t MAX_BUF_SIZE;
|
|
||||||
|
|
||||||
bool readChunkSize
|
|
||||||
(size_t& inbufOffset, const unsigned char* inbuf, size_t inlen);
|
|
||||||
|
|
||||||
bool readTrailer
|
|
||||||
(size_t& inbufOffset, const unsigned char* inbuf, size_t inlen);
|
|
||||||
|
|
||||||
bool readData
|
|
||||||
(ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment);
|
|
||||||
|
|
||||||
bool readDataEnd
|
|
||||||
(size_t& inbufOffset, const unsigned char* inbuf, size_t inlen);
|
|
||||||
|
|
||||||
class ReadChunkSizeStateHandler:public StateHandler {
|
|
||||||
public:
|
|
||||||
virtual bool execute
|
|
||||||
(ChunkedDecodingStreamFilter* filter,
|
|
||||||
ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment) const
|
|
||||||
{
|
|
||||||
return filter->readChunkSize(inbufOffset, inbuf, inlen);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReadTrailerStateHandler:public StateHandler {
|
|
||||||
public:
|
|
||||||
virtual bool execute
|
|
||||||
(ChunkedDecodingStreamFilter* filter,
|
|
||||||
ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment) const
|
|
||||||
{
|
|
||||||
return filter->readTrailer(inbufOffset, inbuf, inlen);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReadDataStateHandler:public StateHandler {
|
|
||||||
public:
|
|
||||||
virtual bool execute
|
|
||||||
(ChunkedDecodingStreamFilter* filter,
|
|
||||||
ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment) const
|
|
||||||
{
|
|
||||||
return filter->readData(outlen, inbufOffset, inbuf, inlen, out, segment);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReadDataEndStateHandler:public StateHandler {
|
|
||||||
public:
|
|
||||||
virtual bool execute
|
|
||||||
(ChunkedDecodingStreamFilter* filter,
|
|
||||||
ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment) const
|
|
||||||
{
|
|
||||||
return filter->readDataEnd(inbufOffset, inbuf, inlen);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class StreamEndStatehandler:public StateHandler {
|
|
||||||
public:
|
|
||||||
virtual bool execute
|
|
||||||
(ChunkedDecodingStreamFilter* filter,
|
|
||||||
ssize_t& outlen,
|
|
||||||
size_t& inbufOffset,
|
|
||||||
const unsigned char* inbuf,
|
|
||||||
size_t inlen,
|
|
||||||
const SharedHandle<BinaryStream>& out,
|
|
||||||
const SharedHandle<Segment>& segment) const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static ReadChunkSizeStateHandler* readChunkSizeStateHandler_;
|
|
||||||
static ReadTrailerStateHandler* readTrailerStateHandler_;
|
|
||||||
static ReadDataStateHandler* readDataStateHandler_;
|
|
||||||
static ReadDataEndStateHandler* readDataEndStateHandler_;
|
|
||||||
static StreamEndStatehandler* streamEndStateHandler_;
|
|
||||||
public:
|
public:
|
||||||
ChunkedDecodingStreamFilter
|
ChunkedDecodingStreamFilter
|
||||||
(const SharedHandle<StreamFilter>& delegate = SharedHandle<StreamFilter>());
|
(const SharedHandle<StreamFilter>& delegate = SharedHandle<StreamFilter>());
|
||||||
|
|
|
@ -57,6 +57,7 @@ public:
|
||||||
// init() must be called before calling decode().
|
// init() must be called before calling decode().
|
||||||
virtual void init() = 0;
|
virtual void init() = 0;
|
||||||
|
|
||||||
|
// Returns the number of bytes written to sink
|
||||||
virtual ssize_t transform(const SharedHandle<BinaryStream>& out,
|
virtual ssize_t transform(const SharedHandle<BinaryStream>& out,
|
||||||
const SharedHandle<Segment>& segment,
|
const SharedHandle<Segment>& segment,
|
||||||
const unsigned char* inbuf, size_t inlen) = 0;
|
const unsigned char* inbuf, size_t inlen) = 0;
|
||||||
|
@ -69,6 +70,8 @@ public:
|
||||||
|
|
||||||
virtual const std::string& getName() const = 0;
|
virtual const std::string& getName() const = 0;
|
||||||
|
|
||||||
|
// Returns the number of input bytes processed in the last
|
||||||
|
// tranfrom() invocation.
|
||||||
virtual size_t getBytesProcessed() const = 0;
|
virtual size_t getBytesProcessed() const = 0;
|
||||||
|
|
||||||
virtual bool installDelegate(const SharedHandle<StreamFilter>& filter);
|
virtual bool installDelegate(const SharedHandle<StreamFilter>& filter);
|
||||||
|
|
|
@ -59,119 +59,144 @@ CPPUNIT_TEST_SUITE_REGISTRATION( ChunkedDecodingStreamFilterTest );
|
||||||
void ChunkedDecodingStreamFilterTest::testTransform()
|
void ChunkedDecodingStreamFilterTest::testTransform()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
{
|
std::basic_string<unsigned char> msg =
|
||||||
std::basic_string<unsigned char> msg =
|
reinterpret_cast<const unsigned char*>("a\r\n1234567890\r\n");
|
||||||
reinterpret_cast<const unsigned char*>("a\r\n1234567890\r\n");
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
CPPUNIT_ASSERT_EQUAL((ssize_t)10, r);
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)10, r);
|
CPPUNIT_ASSERT_EQUAL(std::string("1234567890"), writer_->getString());
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("1234567890"), writer_->getString());
|
CPPUNIT_ASSERT_EQUAL((size_t)15, filter_->getBytesProcessed());
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
// Feed extension; see it is ignored.
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>
|
|
||||||
("3;extensionIgnored\r\n123\r\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)3, r);
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("123"), writer_->getString());
|
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
// Feed 2extensions; see it is ignored.
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>
|
|
||||||
("3;extension1;extension2;\r\n123\r\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)3, r);
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("123"), writer_->getString());
|
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
// Not all chunk size is available
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("1");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("0\r\n1234567890123456\r\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)16, r);
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("1234567890123456"),
|
|
||||||
writer_->getString());
|
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
// Not all chunk data is available
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("10\r\n1234567890");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)10, r);
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("1234567890"), writer_->getString());
|
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("123456\r\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)6, r);
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("123456"), writer_->getString());
|
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
// no trailing CR LF.
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("10\r\n1234567890123456");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)16, r);
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("1234567890123456"),
|
|
||||||
writer_->getString());
|
|
||||||
}
|
|
||||||
clearWriter();
|
|
||||||
// feed only CR
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("\r");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
|
||||||
}
|
|
||||||
// feed next LF
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string(""), writer_->getString());
|
|
||||||
}
|
|
||||||
// feed 0 CR LF.
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("0\r\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
|
||||||
}
|
|
||||||
// feed trailer
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("trailer\r\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
|
||||||
}
|
|
||||||
// feed final CRLF
|
|
||||||
{
|
|
||||||
std::basic_string<unsigned char> msg =
|
|
||||||
reinterpret_cast<const unsigned char*>("\r\n");
|
|
||||||
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
|
||||||
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
|
||||||
}
|
|
||||||
// input is over
|
|
||||||
CPPUNIT_ASSERT(filter_->finished());
|
|
||||||
} catch(DlAbortEx& e) {
|
} catch(DlAbortEx& e) {
|
||||||
CPPUNIT_FAIL(e.stackTrace());
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
}
|
}
|
||||||
|
clearWriter();
|
||||||
|
try {
|
||||||
|
// Feed extension; see it is ignored.
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>
|
||||||
|
("3;extensionIgnored\r\n123\r\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)3, r);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("123"), writer_->getString());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((size_t)25, filter_->getBytesProcessed());
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
clearWriter();
|
||||||
|
// Feed 2extensions; see it is ignored.
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>
|
||||||
|
("3;extension1;extension2;\r\n123\r\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)3, r);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("123"), writer_->getString());
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
clearWriter();
|
||||||
|
// Not all chunk size is available
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("1");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
clearWriter();
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("0\r\n1234567890123456\r\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)16, r);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("1234567890123456"),
|
||||||
|
writer_->getString());
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
clearWriter();
|
||||||
|
// Not all chunk data is available
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("10\r\n1234567890");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)10, r);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("1234567890"), writer_->getString());
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
clearWriter();
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("123456\r\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)6, r);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("123456"), writer_->getString());
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
clearWriter();
|
||||||
|
// no trailing CR LF.
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("10\r\n1234567890123456");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)16, r);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("1234567890123456"),
|
||||||
|
writer_->getString());
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
clearWriter();
|
||||||
|
// feed only CR
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("\r");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
// feed next LF
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string(""), writer_->getString());
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
// feed 0 CR LF.
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("0\r\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
// feed trailer
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("trailer\r\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
// feed final CRLF
|
||||||
|
try {
|
||||||
|
std::basic_string<unsigned char> msg =
|
||||||
|
reinterpret_cast<const unsigned char*>("\r\n");
|
||||||
|
ssize_t r = filter_->transform(writer_, segment_, msg.data(), msg.size());
|
||||||
|
CPPUNIT_ASSERT_EQUAL((ssize_t)0, r);
|
||||||
|
} catch(DlAbortEx& e) {
|
||||||
|
CPPUNIT_FAIL(e.stackTrace());
|
||||||
|
}
|
||||||
|
// input is over
|
||||||
|
CPPUNIT_ASSERT(filter_->finished());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkedDecodingStreamFilterTest::testTransform_withoutTrailer()
|
void ChunkedDecodingStreamFilterTest::testTransform_withoutTrailer()
|
||||||
|
|
Loading…
Reference in New Issue