Rewritten ChunkedDecodingStreamFilter

pull/20/head
Tatsuhiro Tsujikawa 2012-06-18 23:42:46 +09:00
parent f3a5aa3bef
commit 9ba65aea1d
4 changed files with 276 additions and 375 deletions

View File

@ -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() {}

View File

@ -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>());

View File

@ -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);

View File

@ -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()