mirror of https://github.com/aria2/aria2
Rewritten ChunkedDecodingStreamFilter
parent
f3a5aa3bef
commit
9ba65aea1d
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -46,174 +46,164 @@ namespace aria2 {
|
|||
const std::string ChunkedDecodingStreamFilter::NAME
|
||||
("ChunkedDecodingStreamFilter");
|
||||
|
||||
size_t ChunkedDecodingStreamFilter::MAX_BUF_SIZE = 1024*1024;
|
||||
|
||||
ChunkedDecodingStreamFilter::ReadChunkSizeStateHandler*
|
||||
ChunkedDecodingStreamFilter::readChunkSizeStateHandler_ =
|
||||
new ChunkedDecodingStreamFilter::ReadChunkSizeStateHandler();
|
||||
|
||||
ChunkedDecodingStreamFilter::ReadTrailerStateHandler*
|
||||
ChunkedDecodingStreamFilter::readTrailerStateHandler_ =
|
||||
new ChunkedDecodingStreamFilter::ReadTrailerStateHandler();
|
||||
|
||||
ChunkedDecodingStreamFilter::ReadDataStateHandler*
|
||||
ChunkedDecodingStreamFilter::readDataStateHandler_ =
|
||||
new ChunkedDecodingStreamFilter::ReadDataStateHandler();
|
||||
|
||||
ChunkedDecodingStreamFilter::ReadDataEndStateHandler*
|
||||
ChunkedDecodingStreamFilter::readDataEndStateHandler_ =
|
||||
new ChunkedDecodingStreamFilter::ReadDataEndStateHandler();
|
||||
|
||||
ChunkedDecodingStreamFilter::StreamEndStatehandler*
|
||||
ChunkedDecodingStreamFilter::streamEndStateHandler_ =
|
||||
new ChunkedDecodingStreamFilter::StreamEndStatehandler();
|
||||
namespace {
|
||||
enum {
|
||||
PREV_CHUNK_SIZE,
|
||||
CHUNK_SIZE,
|
||||
CHUNK_EXTENSION,
|
||||
PREV_CHUNK_SIZE_LF,
|
||||
CHUNK,
|
||||
PREV_CHUNK_CR,
|
||||
PREV_CHUNK_LF,
|
||||
PREV_TRAILER,
|
||||
TRAILER,
|
||||
PREV_TRAILER_LF,
|
||||
PREV_END_CR,
|
||||
PREV_END_LF,
|
||||
CHUNKS_COMPLETE
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ChunkedDecodingStreamFilter::ChunkedDecodingStreamFilter
|
||||
(const SharedHandle<StreamFilter>& delegate):
|
||||
StreamFilter(delegate),
|
||||
state_(readChunkSizeStateHandler_),
|
||||
state_(PREV_CHUNK_SIZE),
|
||||
chunkSize_(0),
|
||||
chunkRemaining_(0),
|
||||
bytesProcessed_(0) {}
|
||||
|
||||
ChunkedDecodingStreamFilter::~ChunkedDecodingStreamFilter() {}
|
||||
|
||||
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
|
||||
(const SharedHandle<BinaryStream>& out,
|
||||
const SharedHandle<Segment>& segment,
|
||||
const unsigned char* inbuf, size_t inlen)
|
||||
{
|
||||
size_t inbufOffset = 0;
|
||||
ssize_t outlen = 0;
|
||||
while(inbufOffset < inlen) {
|
||||
ssize_t olen = 0;
|
||||
bool r = state_->execute
|
||||
(this, olen, inbufOffset, inbuf, inlen, out, segment);
|
||||
outlen += olen;
|
||||
if(!r) {
|
||||
size_t i;
|
||||
bytesProcessed_ = 0;
|
||||
for(i = 0; i < inlen; ++i) {
|
||||
unsigned char c = inbuf[i];
|
||||
switch(state_) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool ChunkedDecodingStreamFilter::finished()
|
||||
{
|
||||
return state_ == streamEndStateHandler_ && getDelegate()->finished();
|
||||
return state_ == CHUNKS_COMPLETE && getDelegate()->finished();
|
||||
}
|
||||
|
||||
void ChunkedDecodingStreamFilter::release() {}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -41,127 +41,10 @@ namespace aria2 {
|
|||
|
||||
class ChunkedDecodingStreamFilter : public StreamFilter {
|
||||
private:
|
||||
class StateHandler {
|
||||
public:
|
||||
virtual ~StateHandler() {}
|
||||
|
||||
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_;
|
||||
|
||||
int state_;
|
||||
int64_t chunkSize_;
|
||||
int64_t chunkRemaining_;
|
||||
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:
|
||||
ChunkedDecodingStreamFilter
|
||||
(const SharedHandle<StreamFilter>& delegate = SharedHandle<StreamFilter>());
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
// init() must be called before calling decode().
|
||||
virtual void init() = 0;
|
||||
|
||||
// Returns the number of bytes written to sink
|
||||
virtual ssize_t transform(const SharedHandle<BinaryStream>& out,
|
||||
const SharedHandle<Segment>& segment,
|
||||
const unsigned char* inbuf, size_t inlen) = 0;
|
||||
|
@ -69,6 +70,8 @@ public:
|
|||
|
||||
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 bool installDelegate(const SharedHandle<StreamFilter>& filter);
|
||||
|
|
|
@ -59,119 +59,144 @@ CPPUNIT_TEST_SUITE_REGISTRATION( ChunkedDecodingStreamFilterTest );
|
|||
void ChunkedDecodingStreamFilterTest::testTransform()
|
||||
{
|
||||
try {
|
||||
{
|
||||
std::basic_string<unsigned char> msg =
|
||||
reinterpret_cast<const unsigned char*>("a\r\n1234567890\r\n");
|
||||
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();
|
||||
// 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());
|
||||
std::basic_string<unsigned char> msg =
|
||||
reinterpret_cast<const unsigned char*>("a\r\n1234567890\r\n");
|
||||
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());
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)15, filter_->getBytesProcessed());
|
||||
} catch(DlAbortEx& e) {
|
||||
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()
|
||||
|
|
Loading…
Reference in New Issue