summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ChunkedDecodingStreamFilter.cc223
-rw-r--r--src/ChunkedDecodingStreamFilter.h194
-rw-r--r--src/DownloadCommand.cc140
-rw-r--r--src/DownloadCommand.h19
-rw-r--r--src/GZipDecodingStreamFilter.cc131
-rw-r--r--src/GZipDecodingStreamFilter.h81
-rw-r--r--src/HttpConnection.cc2
-rw-r--r--src/HttpDownloadCommand.cc24
-rw-r--r--src/HttpResponse.cc25
-rw-r--r--src/HttpResponse.h6
-rw-r--r--src/HttpResponseCommand.cc120
-rw-r--r--src/HttpResponseCommand.h10
-rw-r--r--src/HttpSkipResponseCommand.cc45
-rw-r--r--src/HttpSkipResponseCommand.h8
-rw-r--r--src/Makefile.am7
-rw-r--r--src/Makefile.in42
-rw-r--r--src/NullSinkStreamFilter.cc41
-rw-r--r--src/NullSinkStreamFilter.h86
-rw-r--r--src/RequestGroup.cc4
-rw-r--r--src/SinkStreamFilter.cc65
-rw-r--r--src/SinkStreamFilter.h84
-rw-r--r--src/StreamFilter.cc53
-rw-r--r--src/StreamFilter.h84
23 files changed, 1290 insertions, 204 deletions
diff --git a/src/ChunkedDecodingStreamFilter.cc b/src/ChunkedDecodingStreamFilter.cc
new file mode 100644
index 00000000..87084707
--- /dev/null
+++ b/src/ChunkedDecodingStreamFilter.cc
@@ -0,0 +1,223 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "ChunkedDecodingStreamFilter.h"
+
+#include <cassert>
+
+#include "util.h"
+#include "message.h"
+#include "DlAbortEx.h"
+#include "StringFormat.h"
+#include "A2STR.h"
+
+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();
+
+ChunkedDecodingStreamFilter::ChunkedDecodingStreamFilter
+(const SharedHandle<StreamFilter>& delegate):
+ StreamFilter(delegate),
+ state_(readChunkSizeStateHandler_),
+ chunkSize_(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::parseULLInt(buf_.substr(0, extPos), 16);
+ 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)
+{
+ uint64_t readlen =
+ std::min(chunkSize_, static_cast<uint64_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(util::startsWith(buf_, A2STR::CRLF)) {
+ 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) {
+ break;
+ }
+ }
+ bytesProcessed_ = inbufOffset;
+ return outlen;
+}
+
+bool ChunkedDecodingStreamFilter::finished()
+{
+ return state_ == streamEndStateHandler_ && getDelegate()->finished();
+}
+
+void ChunkedDecodingStreamFilter::release() {}
+
+const std::string& ChunkedDecodingStreamFilter::getName() const
+{
+ return NAME;
+}
+
+} // namespace aria2
diff --git a/src/ChunkedDecodingStreamFilter.h b/src/ChunkedDecodingStreamFilter.h
new file mode 100644
index 00000000..6909e33e
--- /dev/null
+++ b/src/ChunkedDecodingStreamFilter.h
@@ -0,0 +1,194 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_CHUNKED_DECODING_STREAM_FILTER_H
+#define D_CHUNKED_DECODING_STREAM_FILTER_H
+
+#include "StreamFilter.h"
+
+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_;
+
+ uint64_t chunkSize_;
+
+ 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>());
+
+ virtual ~ChunkedDecodingStreamFilter();
+
+ virtual void init();
+
+ virtual ssize_t transform
+ (const SharedHandle<BinaryStream>& out,
+ const SharedHandle<Segment>& segment,
+ const unsigned char* inbuf, size_t inlen);
+
+ virtual bool finished();
+
+ virtual void release();
+
+ virtual const std::string& getName() const;
+
+ virtual size_t getBytesProcessed() const
+ {
+ return bytesProcessed_;
+ }
+
+ static const std::string NAME;
+};
+
+} // namespace aria2
+
+#endif // D_CHUNKED_DECODING_STREAM_FILTER_H
diff --git a/src/DownloadCommand.cc b/src/DownloadCommand.cc
index f79a3adc..fb00ef38 100644
--- a/src/DownloadCommand.cc
+++ b/src/DownloadCommand.cc
@@ -56,11 +56,11 @@
#include "message.h"
#include "prefs.h"
#include "StringFormat.h"
-#include "Decoder.h"
#include "RequestGroupMan.h"
#include "wallclock.h"
#include "ServerStatMan.h"
#include "FileAllocationEntry.h"
+#include "SinkStreamFilter.h"
#ifdef ENABLE_MESSAGE_DIGEST
# include "MessageDigestHelper.h"
#endif // ENABLE_MESSAGE_DIGEST
@@ -104,6 +104,10 @@ DownloadCommand::DownloadCommand(cuid_t cuid,
peerStat_ = req->initPeerStat();
peerStat_->downloadStart();
getSegmentMan()->registerPeerStat(peerStat_);
+
+ streamFilter_.reset(new SinkStreamFilter(pieceHashValidationEnabled_));
+ streamFilter_->init();
+ sinkFilterOnly_ = true;
}
DownloadCommand::~DownloadCommand() {
@@ -120,80 +124,45 @@ bool DownloadCommand::executeInternal() {
return false;
}
setReadCheckSocket(getSocket());
- SharedHandle<Segment> segment = getSegments().front();
+ const SharedHandle<DiskAdaptor>& diskAdaptor =
+ getPieceStorage()->getDiskAdaptor();
+ SharedHandle<Segment> segment = getSegments().front();
size_t bufSize;
- if(segment->getLength() > 0) {
- if(static_cast<uint64_t>(segment->getPosition()+segment->getLength()) <=
- static_cast<uint64_t>(getFileEntry()->getLastOffset())) {
- bufSize = std::min(segment->getLength()-segment->getWrittenLength(),
- BUFSIZE);
+ if(sinkFilterOnly_) {
+ if(segment->getLength() > 0 ) {
+ if(static_cast<uint64_t>(segment->getPosition()+segment->getLength()) <=
+ static_cast<uint64_t>(getFileEntry()->getLastOffset())) {
+ bufSize = std::min(segment->getLength()-segment->getWrittenLength(),
+ BUFSIZE);
+ } else {
+ bufSize =
+ std::min
+ (static_cast<size_t>
+ (getFileEntry()->getLastOffset()-segment->getPositionToWrite()),
+ BUFSIZE);
+ }
} else {
- bufSize =
- std::min
- (static_cast<size_t>
- (getFileEntry()->getLastOffset()-segment->getPositionToWrite()),
- BUFSIZE);
+ bufSize = BUFSIZE;
}
+ getSocket()->readData(buf_, bufSize);
+ streamFilter_->transform(diskAdaptor, segment, buf_, bufSize);
} else {
+ // It is possible that segment is completed but we have some bytes
+ // of stream to read. For example, chunked encoding has "0"+CRLF
+ // after data. After we read data(at this moment segment is
+ // completed), we need another 3bytes(or more if it has trailers).
bufSize = BUFSIZE;
- }
- // It is possible that segment is completed but we have some bytes
- // of stream to read. For example, chunked encoding has "0"+CRLF
- // after data. After we read data(at this moment segment is
- // completed), we need another 3bytes(or more if it has extension).
- if(bufSize == 0 &&
- ((!transferEncodingDecoder_.isNull() &&
- !transferEncodingDecoder_->finished()) ||
- (!contentEncodingDecoder_.isNull() &&
- !contentEncodingDecoder_->finished()))) {
- bufSize = 1;
- }
- getSocket()->readData(buf_, bufSize);
-
- const SharedHandle<DiskAdaptor>& diskAdaptor =
- getPieceStorage()->getDiskAdaptor();
-
- const unsigned char* bufFinal;
- size_t bufSizeFinal;
-
- std::string decoded;
- if(transferEncodingDecoder_.isNull()) {
- bufFinal = buf_;
- bufSizeFinal = bufSize;
- } else {
- decoded = transferEncodingDecoder_->decode(buf_, bufSize);
-
- bufFinal = reinterpret_cast<const unsigned char*>(decoded.c_str());
- bufSizeFinal = decoded.size();
- }
-
- if(contentEncodingDecoder_.isNull()) {
- diskAdaptor->writeData(bufFinal, bufSizeFinal,
- segment->getPositionToWrite());
- } else {
- std::string out = contentEncodingDecoder_->decode(bufFinal, bufSizeFinal);
- diskAdaptor->writeData(reinterpret_cast<const unsigned char*>(out.data()),
- out.size(),
- segment->getPositionToWrite());
- bufSizeFinal = out.size();
- }
-
-#ifdef ENABLE_MESSAGE_DIGEST
-
- if(pieceHashValidationEnabled_) {
- segment->updateHash(segment->getWrittenLength(), bufFinal, bufSizeFinal);
- }
-
-#endif // ENABLE_MESSAGE_DIGEST
- if(bufSizeFinal > 0) {
- segment->updateWrittenLength(bufSizeFinal);
+ getSocket()->peekData(buf_, bufSize);
+ streamFilter_->transform(diskAdaptor, segment, buf_, bufSize);
+ bufSize = streamFilter_->getBytesProcessed();
+ getSocket()->readData(buf_, bufSize);
}
peerStat_->updateDownloadLength(bufSize);
getSegmentMan()->updateDownloadSpeedFor(peerStat_);
bool segmentPartComplete = false;
// Note that GrowSegment::complete() always returns false.
- if(transferEncodingDecoder_.isNull() && contentEncodingDecoder_.isNull()) {
+ if(sinkFilterOnly_) {
if(segment->complete() ||
segment->getPositionToWrite() == getFileEntry()->getLastOffset()) {
segmentPartComplete = true;
@@ -203,23 +172,20 @@ bool DownloadCommand::executeInternal() {
}
} else {
off_t loff = getFileEntry()->gtoloff(segment->getPositionToWrite());
- if(!transferEncodingDecoder_.isNull() &&
- ((loff == getRequestEndOffset() && transferEncodingDecoder_->finished())
+ if(getFileEntry()->getLength() > 0 && !sinkFilterOnly_ &&
+ ((loff == getRequestEndOffset() && streamFilter_->finished())
|| loff < getRequestEndOffset()) &&
(segment->complete() ||
segment->getPositionToWrite() == getFileEntry()->getLastOffset())) {
- // In this case, transferEncodingDecoder is used and
- // Content-Length is known. We check
- // transferEncodingDecoder_->finished() only if the requested
- // end offset equals to written position in file local offset;
- // in other words, data in the requested ranage is all received.
- // If requested end offset is greater than this segment, then
- // transferEncodingDecoder_ is not finished in this segment.
+ // In this case, StreamFilter other than *SinkStreamFilter is
+ // used and Content-Length is known. We check
+ // streamFilter_->finished() only if the requested end offset
+ // equals to written position in file local offset; in other
+ // words, data in the requested ranage is all received. If
+ // requested end offset is greater than this segment, then
+ // streamFilter_ is not finished in this segment.
segmentPartComplete = true;
- } else if((transferEncodingDecoder_.isNull() ||
- transferEncodingDecoder_->finished()) &&
- (contentEncodingDecoder_.isNull() ||
- contentEncodingDecoder_->finished())) {
+ } else if(streamFilter_->finished()) {
segmentPartComplete = true;
}
}
@@ -392,18 +358,18 @@ void DownloadCommand::validatePieceHash(const SharedHandle<Segment>& segment,
}
}
-#endif // ENABLE_MESSAGE_DIGEST
-
-void DownloadCommand::setTransferEncodingDecoder
-(const SharedHandle<Decoder>& decoder)
+void DownloadCommand::installStreamFilter
+(const SharedHandle<StreamFilter>& streamFilter)
{
- this->transferEncodingDecoder_ = decoder;
+ if(streamFilter.isNull()) {
+ return;
+ }
+ streamFilter->installDelegate(streamFilter_);
+ streamFilter_ = streamFilter;
+ sinkFilterOnly_ =
+ util::endsWith(streamFilter_->getName(), SinkStreamFilter::NAME);
}
-void DownloadCommand::setContentEncodingDecoder
-(const SharedHandle<Decoder>& decoder)
-{
- contentEncodingDecoder_ = decoder;
-}
+#endif // ENABLE_MESSAGE_DIGEST
} // namespace aria2
diff --git a/src/DownloadCommand.h b/src/DownloadCommand.h
index 128b6bf7..13342c70 100644
--- a/src/DownloadCommand.h
+++ b/src/DownloadCommand.h
@@ -39,8 +39,8 @@
namespace aria2 {
-class Decoder;
class PeerStat;
+class StreamFilter;
#ifdef ENABLE_MESSAGE_DIGEST
class MessageDigestContext;
#endif // ENABLE_MESSAGE_DIGEST
@@ -67,9 +67,9 @@ private:
void checkLowestDownloadSpeed() const;
- SharedHandle<Decoder> transferEncodingDecoder_;
+ SharedHandle<StreamFilter> streamFilter_;
- SharedHandle<Decoder> contentEncodingDecoder_;
+ bool sinkFilterOnly_;
protected:
virtual bool executeInternal();
@@ -86,19 +86,12 @@ public:
const SharedHandle<SocketCore>& s);
virtual ~DownloadCommand();
- const SharedHandle<Decoder>& getTransferEncodingDecoder() const
+ const SharedHandle<StreamFilter>& getStreamFilter() const
{
- return transferEncodingDecoder_;
+ return streamFilter_;
}
- void setTransferEncodingDecoder(const SharedHandle<Decoder>& decoder);
-
- const SharedHandle<Decoder>& getContentEncodingDecoder() const
- {
- return contentEncodingDecoder_;
- }
-
- void setContentEncodingDecoder(const SharedHandle<Decoder>& decoder);
+ void installStreamFilter(const SharedHandle<StreamFilter>& streamFilter);
void setStartupIdleTime(time_t startupIdleTime)
{
diff --git a/src/GZipDecodingStreamFilter.cc b/src/GZipDecodingStreamFilter.cc
new file mode 100644
index 00000000..3fa1d05c
--- /dev/null
+++ b/src/GZipDecodingStreamFilter.cc
@@ -0,0 +1,131 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "GZipDecodingStreamFilter.h"
+
+#include <cassert>
+
+#include "StringFormat.h"
+#include "DlAbortEx.h"
+
+namespace aria2 {
+
+const std::string GZipDecodingStreamFilter::NAME("GZipDecodingStreamFilter");
+
+GZipDecodingStreamFilter::GZipDecodingStreamFilter
+(const SharedHandle<StreamFilter>& delegate):
+ StreamFilter(delegate), strm_(0), finished_(false), bytesProcessed_(0) {}
+
+GZipDecodingStreamFilter::~GZipDecodingStreamFilter()
+{
+ release();
+}
+
+void GZipDecodingStreamFilter::init()
+{
+ finished_ = false;
+ release();
+ strm_ = new z_stream();
+ strm_->zalloc = Z_NULL;
+ strm_->zfree = Z_NULL;
+ strm_->opaque = Z_NULL;
+ strm_->avail_in = 0;
+ strm_->next_in = Z_NULL;
+
+ // initalize z_stream with gzip/zlib format auto detection enabled.
+ if(Z_OK != inflateInit2(strm_, 47)) {
+ throw DL_ABORT_EX("Initializing z_stream failed.");
+ }
+}
+
+void GZipDecodingStreamFilter::release()
+{
+ if(strm_) {
+ inflateEnd(strm_);
+ delete strm_;
+ strm_ = 0;
+ }
+}
+
+ssize_t GZipDecodingStreamFilter::transform
+(const SharedHandle<BinaryStream>& out,
+ const SharedHandle<Segment>& segment,
+ const unsigned char* inbuf, size_t inlen)
+{
+ bytesProcessed_ = 0;
+ ssize_t outlen = 0;
+ if(inlen == 0) {
+ return outlen;
+ }
+
+ strm_->avail_in = inlen;
+ strm_->next_in = const_cast<unsigned char*>(inbuf);
+
+ unsigned char outbuf[OUTBUF_LENGTH];
+ while(1) {
+ strm_->avail_out = OUTBUF_LENGTH;
+ strm_->next_out = outbuf;
+
+ int ret = ::inflate(strm_, Z_NO_FLUSH);
+
+ if(ret == Z_STREAM_END) {
+ finished_ = true;
+ } else if(ret != Z_OK) {
+ throw DL_ABORT_EX(StringFormat("libz::inflate() failed. cause:%s",
+ strm_->msg).str());
+ }
+
+ size_t produced = OUTBUF_LENGTH-strm_->avail_out;
+
+ outlen += getDelegate()->transform(out, segment, outbuf, produced);
+ if(strm_->avail_out > 0) {
+ break;
+ }
+ }
+ assert(inlen >= strm_->avail_in);
+ bytesProcessed_ = inlen-strm_->avail_in;
+ return outlen;
+}
+
+bool GZipDecodingStreamFilter::finished()
+{
+ return finished_ && getDelegate()->finished();
+}
+
+const std::string& GZipDecodingStreamFilter::getName() const
+{
+ return NAME;
+}
+
+} // namespace aria2
diff --git a/src/GZipDecodingStreamFilter.h b/src/GZipDecodingStreamFilter.h
new file mode 100644
index 00000000..3fce057f
--- /dev/null
+++ b/src/GZipDecodingStreamFilter.h
@@ -0,0 +1,81 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_GZIP_STREAM_FILTER_H
+#define D_GZIP_STREAM_FILTER_H
+
+#include "StreamFilter.h"
+#include <zlib.h>
+
+namespace aria2 {
+
+// GZipDecodingStreamFilter can decode both gzip and deflate format.
+class GZipDecodingStreamFilter : public StreamFilter {
+private:
+ z_stream* strm_;
+
+ bool finished_;
+
+ size_t bytesProcessed_;
+
+ static const size_t OUTBUF_LENGTH = 16*1024;
+public:
+ GZipDecodingStreamFilter
+ (const SharedHandle<StreamFilter>& delegate = SharedHandle<StreamFilter>());
+
+ virtual ~GZipDecodingStreamFilter();
+
+ virtual void init();
+
+ virtual ssize_t transform(const SharedHandle<BinaryStream>& out,
+ const SharedHandle<Segment>& segment,
+ const unsigned char* inbuf, size_t inlen);
+
+ virtual bool finished();
+
+ virtual void release();
+
+ virtual const std::string& getName() const;
+
+ virtual size_t getBytesProcessed() const
+ {
+ return bytesProcessed_;
+ }
+
+ static const std::string NAME;
+};
+
+} // namespace aria2
+
+#endif // D_GZIP_STREAM_FILTER_H
diff --git a/src/HttpConnection.cc b/src/HttpConnection.cc
index ff995a08..56710e62 100644
--- a/src/HttpConnection.cc
+++ b/src/HttpConnection.cc
@@ -134,7 +134,7 @@ SharedHandle<HttpResponse> HttpConnection::receiveResponse()
if(socket_->wantRead() || socket_->wantWrite()) {
return SharedHandle<HttpResponse>();
} else {
- throw DL_RETRY_EX(EX_INVALID_RESPONSE);
+ throw DL_RETRY_EX(EX_GOT_EOF);
}
}
proc->update(buf, size);
diff --git a/src/HttpDownloadCommand.cc b/src/HttpDownloadCommand.cc
index 52ba1ed7..7a304682 100644
--- a/src/HttpDownloadCommand.cc
+++ b/src/HttpDownloadCommand.cc
@@ -47,12 +47,15 @@
#include "HttpHeader.h"
#include "Range.h"
#include "DownloadContext.h"
-#include "Decoder.h"
#include "RequestGroupMan.h"
#include "FileAllocationEntry.h"
#include "CheckIntegrityEntry.h"
#include "ServerStatMan.h"
#include "Logger.h"
+#include "StreamFilter.h"
+#include "SinkStreamFilter.h"
+#include "util.h"
+
namespace aria2 {
HttpDownloadCommand::HttpDownloadCommand
@@ -88,18 +91,16 @@ bool HttpDownloadCommand::prepareForNextSegment() {
if(getRequest()->isPipeliningEnabled() ||
(getRequest()->isKeepAliveEnabled() &&
(
- // Make sure that all decoders are finished to pool socket
- ((!getTransferEncodingDecoder().isNull() &&
- getTransferEncodingDecoder()->finished()) ||
- (getTransferEncodingDecoder().isNull() &&
- !getContentEncodingDecoder().isNull() &&
- getContentEncodingDecoder()->finished())) ||
+ // Make sure that all filters are finished to pool socket
+ (!util::endsWith(getStreamFilter()->getName(),
+ SinkStreamFilter::NAME) &&
+ getStreamFilter()->finished()) ||
getRequestEndOffset() ==
getFileEntry()->gtoloff(getSegments().front()->getPositionToWrite())
)
)
) {
- // TODO What if server sends EOF when _contentEncodingDecoder is
+ // TODO What if server sends EOF when non-SinkStreamFilter is
// used and server didn't send Connection: close? We end up to
// pool terminated socket. In HTTP/1.1, keep-alive is default,
// so closing connection without Connection: close header means
@@ -133,7 +134,12 @@ bool HttpDownloadCommand::prepareForNextSegment() {
off_t HttpDownloadCommand::getRequestEndOffset() const
{
- return httpResponse_->getHttpHeader()->getRange()->getEndByte()+1;
+ off_t endByte = httpResponse_->getHttpHeader()->getRange()->getEndByte();
+ if(endByte > 0) {
+ return endByte+1;
+ } else {
+ return endByte;
+ }
}
} // namespace aria2
diff --git a/src/HttpResponse.cc b/src/HttpResponse.cc
index e2b08fb1..fd406b4a 100644
--- a/src/HttpResponse.cc
+++ b/src/HttpResponse.cc
@@ -46,14 +46,13 @@
#include "DlRetryEx.h"
#include "StringFormat.h"
#include "A2STR.h"
-#include "Decoder.h"
-#include "ChunkedDecoder.h"
-#ifdef HAVE_LIBZ
-# include "GZipDecoder.h"
-#endif // HAVE_LIBZ
#include "CookieStorage.h"
#include "AuthConfigFactory.h"
#include "AuthConfig.h"
+#include "ChunkedDecodingStreamFilter.h"
+#ifdef HAVE_LIBZ
+# include "GZipDecodingStreamFilter.h"
+#endif // HAVE_LIBZ
namespace aria2 {
@@ -177,20 +176,21 @@ bool HttpResponse::isTransferEncodingSpecified() const
std::string HttpResponse::getTransferEncoding() const
{
- // TODO See TODO in getTransferEncodingDecoder()
+ // TODO See TODO in getTransferEncodingStreamFilter()
return httpHeader_->getFirst(HttpHeader::TRANSFER_ENCODING);
}
-SharedHandle<Decoder> HttpResponse::getTransferEncodingDecoder() const
+SharedHandle<StreamFilter> HttpResponse::getTransferEncodingStreamFilter() const
{
+ SharedHandle<StreamFilter> filter;
// TODO Transfer-Encoding header field can contains multiple tokens. We should
// parse the field and retrieve each token.
if(isTransferEncodingSpecified()) {
if(getTransferEncoding() == HttpHeader::CHUNKED) {
- return SharedHandle<Decoder>(new ChunkedDecoder());
+ filter.reset(new ChunkedDecodingStreamFilter());
}
}
- return SharedHandle<Decoder>();
+ return filter;
}
bool HttpResponse::isContentEncodingSpecified() const
@@ -203,15 +203,16 @@ const std::string& HttpResponse::getContentEncoding() const
return httpHeader_->getFirst(HttpHeader::CONTENT_ENCODING);
}
-SharedHandle<Decoder> HttpResponse::getContentEncodingDecoder() const
+SharedHandle<StreamFilter> HttpResponse::getContentEncodingStreamFilter() const
{
+ SharedHandle<StreamFilter> filter;
#ifdef HAVE_LIBZ
if(getContentEncoding() == HttpHeader::GZIP ||
getContentEncoding() == HttpHeader::DEFLATE) {
- return SharedHandle<Decoder>(new GZipDecoder());
+ filter.reset(new GZipDecodingStreamFilter());
}
#endif // HAVE_LIBZ
- return SharedHandle<Decoder>();
+ return filter;
}
uint64_t HttpResponse::getContentLength() const
diff --git a/src/HttpResponse.h b/src/HttpResponse.h
index d3b96281..965bd431 100644
--- a/src/HttpResponse.h
+++ b/src/HttpResponse.h
@@ -48,7 +48,7 @@ namespace aria2 {
class HttpRequest;
class HttpHeader;
class Logger;
-class Decoder;
+class StreamFilter;
class HttpResponse {
private:
@@ -86,13 +86,13 @@ public:
std::string getTransferEncoding() const;
- SharedHandle<Decoder> getTransferEncodingDecoder() const;
+ SharedHandle<StreamFilter> getTransferEncodingStreamFilter() const;
bool isContentEncodingSpecified() const;
const std::string& getContentEncoding() const;
- SharedHandle<Decoder> getContentEncodingDecoder() const;
+ SharedHandle<StreamFilter> getContentEncodingStreamFilter() const;
uint64_t getContentLength() const;
diff --git a/src/HttpResponseCommand.cc b/src/HttpResponseCommand.cc
index a1c8c2aa..bfa67430 100644
--- a/src/HttpResponseCommand.cc
+++ b/src/HttpResponseCommand.cc
@@ -69,14 +69,20 @@
#include "ServerStatMan.h"
#include "FileAllocationEntry.h"
#include "CheckIntegrityEntry.h"
+#include "StreamFilter.h"
+#include "SinkStreamFilter.h"
+#include "ChunkedDecodingStreamFilter.h"
+#include "GZipDecodingStreamFilter.h"
namespace aria2 {
-static SharedHandle<Decoder> getTransferEncodingDecoder
-(const SharedHandle<HttpResponse>& httpResponse);
+static SharedHandle<StreamFilter> getTransferEncodingStreamFilter
+(const SharedHandle<HttpResponse>& httpResponse,
+ const SharedHandle<StreamFilter>& delegate = SharedHandle<StreamFilter>());
-static SharedHandle<Decoder> getContentEncodingDecoder
-(const SharedHandle<HttpResponse>& httpResponse);
+static SharedHandle<StreamFilter> getContentEncodingStreamFilter
+(const SharedHandle<HttpResponse>& httpResponse,
+ const SharedHandle<StreamFilter>& delegate = SharedHandle<StreamFilter>());
HttpResponseCommand::HttpResponseCommand
(cuid_t cuid,
@@ -198,12 +204,16 @@ bool HttpResponseCommand::executeInternal()
// anyway.
getPieceStorage()->getDiskAdaptor()->truncate(0);
getDownloadEngine()->addCommand
- (createHttpDownloadCommand(httpResponse,
- getTransferEncodingDecoder(httpResponse),
- getContentEncodingDecoder(httpResponse)));
+ (createHttpDownloadCommand
+ (httpResponse,
+ getTransferEncodingStreamFilter
+ (httpResponse,
+ getContentEncodingStreamFilter(httpResponse))));
} else {
- getDownloadEngine()->addCommand(createHttpDownloadCommand
- (httpResponse, getTransferEncodingDecoder(httpResponse)));
+ getDownloadEngine()->addCommand
+ (createHttpDownloadCommand
+ (httpResponse,
+ getTransferEncodingStreamFilter(httpResponse)));
}
return true;
}
@@ -275,7 +285,8 @@ bool HttpResponseCommand::handleDefaultEncoding
!segment.isNull() && segment->getPositionToWrite() == 0 &&
!getRequest()->isPipeliningEnabled()) {
command = createHttpDownloadCommand
- (httpResponse, getTransferEncodingDecoder(httpResponse));
+ (httpResponse,
+ getTransferEncodingStreamFilter(httpResponse));
} else {
getSegmentMan()->cancelSegment(getCuid());
getFileEntry()->poolRequest(getRequest());
@@ -291,39 +302,49 @@ bool HttpResponseCommand::handleDefaultEncoding
return true;
}
-static SharedHandle<Decoder> getTransferEncodingDecoder
-(const SharedHandle<HttpResponse>& httpResponse)
+static SharedHandle<StreamFilter> getTransferEncodingStreamFilter
+(const SharedHandle<HttpResponse>& httpResponse,
+ const SharedHandle<StreamFilter>& delegate)
{
- SharedHandle<Decoder> decoder;
+ SharedHandle<StreamFilter> filter;
if(httpResponse->isTransferEncodingSpecified()) {
- decoder = httpResponse->getTransferEncodingDecoder();
- if(decoder.isNull()) {
+ filter = httpResponse->getTransferEncodingStreamFilter();
+ if(filter.isNull()) {
throw DL_ABORT_EX
(StringFormat(EX_TRANSFER_ENCODING_NOT_SUPPORTED,
httpResponse->getTransferEncoding().c_str()).str());
}
- decoder->init();
+ filter->init();
+ filter->installDelegate(delegate);
+ }
+ if(filter.isNull()) {
+ filter = delegate;
}
- return decoder;
+ return filter;
}
-static SharedHandle<Decoder> getContentEncodingDecoder
-(const SharedHandle<HttpResponse>& httpResponse)
+static SharedHandle<StreamFilter> getContentEncodingStreamFilter
+(const SharedHandle<HttpResponse>& httpResponse,
+ const SharedHandle<StreamFilter>& delegate)
{
- SharedHandle<Decoder> decoder;
+ SharedHandle<StreamFilter> filter;
if(httpResponse->isContentEncodingSpecified()) {
- decoder = httpResponse->getContentEncodingDecoder();
- if(decoder.isNull()) {
+ filter = httpResponse->getContentEncodingStreamFilter();
+ if(filter.isNull()) {
LogFactory::getInstance()->info
("Content-Encoding %s is specified, but the current implementation"
"doesn't support it. The decoding process is skipped and the"
"downloaded content will be still encoded.",
httpResponse->getContentEncoding().c_str());
} else {
- decoder->init();
+ filter->init();
+ filter->installDelegate(delegate);
}
}
- return decoder;
+ if(filter.isNull()) {
+ filter = delegate;
+ }
+ return filter;
}
bool HttpResponseCommand::handleOtherEncoding
@@ -357,9 +378,20 @@ bool HttpResponseCommand::handleOtherEncoding
getRequestGroup()->shouldCancelDownloadForSafety();
getRequestGroup()->initPieceStorage();
getPieceStorage()->getDiskAdaptor()->initAndOpenFile();
+
+ SharedHandle<StreamFilter> streamFilter =
+ getTransferEncodingStreamFilter
+ (httpResponse,
+ getContentEncodingStreamFilter(httpResponse));
+
// In this context, knowsTotalLength() is true only when the file is
// really zero-length.
- if(getDownloadContext()->knowsTotalLength()) {
+ if(getDownloadContext()->knowsTotalLength() &&
+ (streamFilter.isNull() ||
+ streamFilter->getName() != ChunkedDecodingStreamFilter::NAME)) {
+ // If chunked transfer-encoding is specified, we have to read end
+ // of chunk markers(0\r\n\r\n, for example), so cannot pool
+ // connection here.
poolConnection();
return true;
}
@@ -369,16 +401,15 @@ bool HttpResponseCommand::handleOtherEncoding
getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
getDownloadEngine()->addCommand
- (createHttpDownloadCommand(httpResponse,
- getTransferEncodingDecoder(httpResponse),
- getContentEncodingDecoder(httpResponse)));
+ (createHttpDownloadCommand(httpResponse, streamFilter));
return true;
}
bool HttpResponseCommand::skipResponseBody
(const SharedHandle<HttpResponse>& httpResponse)
{
- SharedHandle<Decoder> decoder = getTransferEncodingDecoder(httpResponse);
+ SharedHandle<StreamFilter> filter =
+ getTransferEncodingStreamFilter(httpResponse);
// We don't use Content-Encoding here because this response body is just
// thrown away.
@@ -386,7 +417,7 @@ bool HttpResponseCommand::skipResponseBody
(getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
httpConnection_, httpResponse,
getDownloadEngine(), getSocket());
- command->setTransferEncodingDecoder(decoder);
+ command->installStreamFilter(filter);
// If request method is HEAD or the response body is zero-length,
// set command's status to real time so that avoid read check blocking
@@ -403,10 +434,23 @@ bool HttpResponseCommand::skipResponseBody
return true;
}
+static bool decideFileAllocation
+(const SharedHandle<StreamFilter>& filter)
+{
+ for(SharedHandle<StreamFilter> f = filter; !f.isNull(); f = f->getDelegate()){
+ // Since the compressed file's length are returned in the response header
+ // and the decompressed file size is unknown at this point, disable file
+ // allocation here.
+ if(f->getName() == GZipDecodingStreamFilter::NAME) {
+ return false;
+ }
+ }
+ return true;
+}
+
HttpDownloadCommand* HttpResponseCommand::createHttpDownloadCommand
(const SharedHandle<HttpResponse>& httpResponse,
- const SharedHandle<Decoder>& transferEncodingDecoder,
- const SharedHandle<Decoder>& contentEncodingDecoder)
+ const SharedHandle<StreamFilter>& filter)
{
HttpDownloadCommand* command =
@@ -417,16 +461,8 @@ HttpDownloadCommand* HttpResponseCommand::createHttpDownloadCommand
command->setStartupIdleTime(getOption()->getAsInt(PREF_STARTUP_IDLE_TIME));
command->setLowestDownloadSpeedLimit
(getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
- command->setTransferEncodingDecoder(transferEncodingDecoder);
-
- if(!contentEncodingDecoder.isNull()) {
- command->setContentEncodingDecoder(contentEncodingDecoder);
- // Since the compressed file's length are returned in the response header
- // and the decompressed file size is unknown at this point, disable file
- // allocation here.
- getRequestGroup()->setFileAllocationEnabled(false);
- }
-
+ command->installStreamFilter(filter);
+ getRequestGroup()->setFileAllocationEnabled(decideFileAllocation(filter));
getRequestGroup()->getURISelector()->tuneDownloadCommand
(getFileEntry()->getRemainingUris(), command);
diff --git a/src/HttpResponseCommand.h b/src/HttpResponseCommand.h
index 79f2618b..85932f68 100644
--- a/src/HttpResponseCommand.h
+++ b/src/HttpResponseCommand.h
@@ -36,7 +36,6 @@
#define _D_HTTP_RESPONSE_COMMAND_H_
#include "AbstractCommand.h"
-#include "Decoder.h"
#include "TimeA2.h"
namespace aria2 {
@@ -45,6 +44,7 @@ class HttpConnection;
class HttpDownloadCommand;
class HttpResponse;
class SocketCore;
+class StreamFilter;
class HttpResponseCommand : public AbstractCommand {
private:
@@ -55,11 +55,9 @@ private:
bool skipResponseBody(const SharedHandle<HttpResponse>& httpResponse);
HttpDownloadCommand*
- createHttpDownloadCommand(const SharedHandle<HttpResponse>& httpResponse,
- const SharedHandle<Decoder>& transferEncodingDecoder
- = SharedHandle<Decoder>(),
- const SharedHandle<Decoder>& contentEncodingDecoder
- = SharedHandle<Decoder>());
+ createHttpDownloadCommand
+ (const SharedHandle<HttpResponse>& httpResponse,
+ const SharedHandle<StreamFilter>& streamFilter);
void updateLastModifiedTime(const Time& lastModified);
diff --git a/src/HttpSkipResponseCommand.cc b/src/HttpSkipResponseCommand.cc
index 43ad841e..6f0a173e 100644
--- a/src/HttpSkipResponseCommand.cc
+++ b/src/HttpSkipResponseCommand.cc
@@ -37,7 +37,6 @@
#include "HttpResponse.h"
#include "message.h"
#include "SocketCore.h"
-#include "Decoder.h"
#include "DlRetryEx.h"
#include "Request.h"
#include "DownloadEngine.h"
@@ -58,6 +57,10 @@
#include "FileAllocationEntry.h"
#include "CheckIntegrityEntry.h"
#include "ServerStatMan.h"
+#include "StreamFilter.h"
+#include "BinaryStream.h"
+#include "NullSinkStreamFilter.h"
+#include "SinkStreamFilter.h"
namespace aria2 {
@@ -73,22 +76,30 @@ HttpSkipResponseCommand::HttpSkipResponseCommand
AbstractCommand(cuid, req, fileEntry, requestGroup, e, s),
httpConnection_(httpConnection),
httpResponse_(httpResponse),
+ streamFilter_(new NullSinkStreamFilter()),
+ sinkFilterOnly_(true),
totalLength_(httpResponse_->getEntityLength()),
receivedBytes_(0)
{}
HttpSkipResponseCommand::~HttpSkipResponseCommand() {}
-void HttpSkipResponseCommand::setTransferEncodingDecoder
-(const SharedHandle<Decoder>& decoder)
+void HttpSkipResponseCommand::installStreamFilter
+(const SharedHandle<StreamFilter>& streamFilter)
{
- transferEncodingDecoder_ = decoder;
+ if(streamFilter.isNull()) {
+ return;
+ }
+ streamFilter->installDelegate(streamFilter_);
+ streamFilter_ = streamFilter;
+ sinkFilterOnly_ =
+ util::endsWith(streamFilter_->getName(), SinkStreamFilter::NAME);
}
bool HttpSkipResponseCommand::executeInternal()
{
if(getRequest()->getMethod() == Request::METHOD_HEAD ||
- (totalLength_ == 0 && transferEncodingDecoder_.isNull())) {
+ (totalLength_ == 0 && sinkFilterOnly_)) {
// If request method is HEAD or content-length header is present and
// it's value is 0, then pool socket for reuse.
// If content-length header is not present, then EOF is expected in the end.
@@ -101,17 +112,25 @@ bool HttpSkipResponseCommand::executeInternal()
}
const size_t BUFSIZE = 16*1024;
unsigned char buf[BUFSIZE];
- size_t bufSize = BUFSIZE;
-
+ size_t bufSize;
+ if(sinkFilterOnly_ && totalLength_ > 0) {
+ bufSize = totalLength_-receivedBytes_;
+ } else {
+ bufSize = BUFSIZE;
+ }
try {
- getSocket()->readData(buf, bufSize);
-
- if(transferEncodingDecoder_.isNull()) {
+ if(sinkFilterOnly_) {
+ getSocket()->readData(buf, bufSize);
receivedBytes_ += bufSize;
} else {
+ getSocket()->peekData(buf, bufSize);
// receivedBytes_ is not updated if transferEncoding is set.
// The return value is safely ignored here.
- transferEncodingDecoder_->decode(buf, bufSize);
+ streamFilter_->transform(SharedHandle<BinaryStream>(),
+ SharedHandle<Segment>(),
+ buf, bufSize);
+ bufSize = streamFilter_->getBytesProcessed();
+ getSocket()->readData(buf, bufSize);
}
if(totalLength_ != 0 && bufSize == 0 &&
!getSocket()->wantRead() && !getSocket()->wantWrite()) {
@@ -125,7 +144,7 @@ bool HttpSkipResponseCommand::executeInternal()
}
bool finished = false;
- if(transferEncodingDecoder_.isNull()) {
+ if(sinkFilterOnly_) {
if(bufSize == 0) {
if(!getSocket()->wantRead() && !getSocket()->wantWrite()) {
return processResponse();
@@ -134,7 +153,7 @@ bool HttpSkipResponseCommand::executeInternal()
finished = (totalLength_ == receivedBytes_);
}
} else {
- finished = transferEncodingDecoder_->finished();
+ finished = streamFilter_->finished();
}
if(finished) {
poolConnection();
diff --git a/src/HttpSkipResponseCommand.h b/src/HttpSkipResponseCommand.h
index 2a865cf7..7fb1769d 100644
--- a/src/HttpSkipResponseCommand.h
+++ b/src/HttpSkipResponseCommand.h
@@ -41,7 +41,7 @@ namespace aria2 {
class HttpConnection;
class HttpResponse;
-class Decoder;
+class StreamFilter;
class HttpSkipResponseCommand : public AbstractCommand {
private:
@@ -49,7 +49,9 @@ private:
SharedHandle<HttpResponse> httpResponse_;
- SharedHandle<Decoder> transferEncodingDecoder_;
+ SharedHandle<StreamFilter> streamFilter_;
+
+ bool sinkFilterOnly_;
uint64_t totalLength_;
@@ -72,7 +74,7 @@ public:
virtual ~HttpSkipResponseCommand();
- void setTransferEncodingDecoder(const SharedHandle<Decoder>& decoder);
+ void installStreamFilter(const SharedHandle<StreamFilter>& streamFilter);
void disableSocketCheck();
};
diff --git a/src/Makefile.am b/src/Makefile.am
index e294b6c4..813c96a1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -206,7 +206,12 @@ SRCS = Socket.h\
ValueBase.cc ValueBase.h\
ContextAttribute.h\
TorrentAttribute.h\
- AdaptiveFileAllocationIterator.cc AdaptiveFileAllocationIterator.h
+ AdaptiveFileAllocationIterator.cc AdaptiveFileAllocationIterator.h\
+ StreamFilter.cc StreamFilter.h\
+ SinkStreamFilter.cc SinkStreamFilter.h\
+ GZipDecodingStreamFilter.cc GZipDecodingStreamFilter.h\
+ ChunkedDecodingStreamFilter.cc ChunkedDecodingStreamFilter.h\
+ NullSinkStreamFilter.cc NullSinkStreamFilter.h
if ENABLE_XML_RPC
SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\
diff --git a/src/Makefile.in b/src/Makefile.in
index d38f6659..af897bba 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -442,7 +442,11 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
MetadataInfo.h SessionSerializer.cc SessionSerializer.h \
Event.h timespec.h ValueBase.cc ValueBase.h ContextAttribute.h \
TorrentAttribute.h AdaptiveFileAllocationIterator.cc \
- AdaptiveFileAllocationIterator.h \
+ AdaptiveFileAllocationIterator.h StreamFilter.cc \
+ StreamFilter.h SinkStreamFilter.cc SinkStreamFilter.h \
+ GZipDecodingStreamFilter.cc GZipDecodingStreamFilter.h \
+ ChunkedDecodingStreamFilter.cc ChunkedDecodingStreamFilter.h \
+ NullSinkStreamFilter.cc NullSinkStreamFilter.h \
XmlRpcRequestParserController.cc \
XmlRpcRequestParserController.h \
XmlRpcRequestParserStateMachine.cc \
@@ -883,17 +887,20 @@ am__objects_32 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
CreateRequestCommand.$(OBJEXT) download_helper.$(OBJEXT) \
MetadataInfo.$(OBJEXT) SessionSerializer.$(OBJEXT) \
ValueBase.$(OBJEXT) AdaptiveFileAllocationIterator.$(OBJEXT) \
- $(am__objects_1) $(am__objects_2) $(am__objects_3) \
- $(am__objects_4) $(am__objects_5) $(am__objects_6) \
- $(am__objects_7) $(am__objects_8) $(am__objects_9) \
- $(am__objects_10) $(am__objects_11) $(am__objects_12) \
- $(am__objects_13) $(am__objects_14) $(am__objects_15) \
- $(am__objects_16) $(am__objects_17) $(am__objects_18) \
- $(am__objects_19) $(am__objects_20) $(am__objects_21) \
- $(am__objects_22) $(am__objects_23) $(am__objects_24) \
- $(am__objects_25) $(am__objects_26) $(am__objects_27) \
- $(am__objects_28) $(am__objects_29) $(am__objects_30) \
- $(am__objects_31)
+ StreamFilter.$(OBJEXT) SinkStreamFilter.$(OBJEXT) \
+ GZipDecodingStreamFilter.$(OBJEXT) \
+ ChunkedDecodingStreamFilter.$(OBJEXT) \
+ NullSinkStreamFilter.$(OBJEXT) $(am__objects_1) \
+ $(am__objects_2) $(am__objects_3) $(am__objects_4) \
+ $(am__objects_5) $(am__objects_6) $(am__objects_7) \
+ $(am__objects_8) $(am__objects_9) $(am__objects_10) \
+ $(am__objects_11) $(am__objects_12) $(am__objects_13) \
+ $(am__objects_14) $(am__objects_15) $(am__objects_16) \
+ $(am__objects_17) $(am__objects_18) $(am__objects_19) \
+ $(am__objects_20) $(am__objects_21) $(am__objects_22) \
+ $(am__objects_23) $(am__objects_24) $(am__objects_25) \
+ $(am__objects_26) $(am__objects_27) $(am__objects_28) \
+ $(am__objects_29) $(am__objects_30) $(am__objects_31)
am_libaria2c_a_OBJECTS = $(am__objects_32)
libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS)
am__installdirs = "$(DESTDIR)$(bindir)"
@@ -1227,7 +1234,11 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \
MetadataInfo.h SessionSerializer.cc SessionSerializer.h \
Event.h timespec.h ValueBase.cc ValueBase.h ContextAttribute.h \
TorrentAttribute.h AdaptiveFileAllocationIterator.cc \
- AdaptiveFileAllocationIterator.h $(am__append_1) \
+ AdaptiveFileAllocationIterator.h StreamFilter.cc \
+ StreamFilter.h SinkStreamFilter.cc SinkStreamFilter.h \
+ GZipDecodingStreamFilter.cc GZipDecodingStreamFilter.h \
+ ChunkedDecodingStreamFilter.cc ChunkedDecodingStreamFilter.h \
+ NullSinkStreamFilter.cc NullSinkStreamFilter.h $(am__append_1) \
$(am__append_2) $(am__append_3) $(am__append_4) \
$(am__append_5) $(am__append_6) $(am__append_7) \
$(am__append_8) $(am__append_9) $(am__append_10) \
@@ -1396,6 +1407,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CheckIntegrityEntry.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChecksumCheckIntegrityEntry.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChunkedDecoder.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChunkedDecodingStreamFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Command.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConsoleStatCalc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ContentTypeRequestGroupCriteria.Po@am__quote@
@@ -1498,6 +1510,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FtpTunnelRequestCommand.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FtpTunnelResponseCommand.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GZipDecoder.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GZipDecodingStreamFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GZipEncoder.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrowSegment.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HandshakeExtensionMessage.Po@am__quote@
@@ -1559,6 +1572,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Netrc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetrcAuthResolver.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NsCookieParser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NullSinkStreamFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Option.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OptionHandler.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OptionHandlerException.Po@am__quote@
@@ -1606,6 +1620,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SimpleLogFormatter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SimpleRandomizer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SingleFileAllocationIterator.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SinkStreamFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SleepCommand.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SocketBuffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SocketCore.Po@am__quote@
@@ -1614,6 +1629,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Sqlite3CookieParserImpl.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StreamCheckIntegrityEntry.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StreamFileAllocationEntry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StreamFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StringFormat.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TimeA2.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TimeBasedCommand.Po@am__quote@
diff --git a/src/NullSinkStreamFilter.cc b/src/NullSinkStreamFilter.cc
new file mode 100644
index 00000000..4572c575
--- /dev/null
+++ b/src/NullSinkStreamFilter.cc
@@ -0,0 +1,41 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "NullSinkStreamFilter.h"
+
+namespace aria2 {
+
+const std::string NullSinkStreamFilter::NAME("NullSinkStreamFilter");
+
+} // namespace aria2
diff --git a/src/NullSinkStreamFilter.h b/src/NullSinkStreamFilter.h
new file mode 100644
index 00000000..23d59c86
--- /dev/null
+++ b/src/NullSinkStreamFilter.h
@@ -0,0 +1,86 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_NULL_SINK_STREAM_FILTER_H
+#define D_NULL_SINK_STREAM_FILTER_H
+
+#include "StreamFilter.h"
+
+namespace aria2 {
+
+class NullSinkStreamFilter:public StreamFilter {
+private:
+ size_t bytesProcessed_;
+public:
+ NullSinkStreamFilter():bytesProcessed_(0) {}
+
+ virtual void init() {}
+
+ virtual ssize_t transform
+ (const SharedHandle<BinaryStream>& out,
+ const SharedHandle<Segment>& segment,
+ const unsigned char* inbuf, size_t inlen)
+ {
+ bytesProcessed_ = inlen;
+ return bytesProcessed_;
+ }
+
+ virtual bool finished()
+ {
+ return true;
+ }
+
+ virtual void release() {}
+
+ virtual const std::string& getName() const
+ {
+ return NAME;
+ }
+
+ static const std::string NAME;
+
+ virtual size_t getBytesProcessed() const
+ {
+ return bytesProcessed_;
+ }
+
+ virtual bool installDelegate(const SharedHandle<StreamFilter>& filter)
+ {
+ return false;
+ }
+};
+
+} // namespace aria2
+
+#endif // D_NULL_SINK_STREAM_FILTER_H
diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc
index 1e79bda6..7835782d 100644
--- a/src/RequestGroup.cc
+++ b/src/RequestGroup.cc
@@ -525,7 +525,9 @@ void RequestGroup::processCheckIntegrityEntry
void RequestGroup::initPieceStorage()
{
SharedHandle<PieceStorage> tempPieceStorage;
- if(downloadContext_->knowsTotalLength()) {
+ if(downloadContext_->knowsTotalLength() &&
+ (downloadContext_->getTotalLength() > 0 ||
+ downloadContext_->hasAttribute(bittorrent::BITTORRENT))) {
#ifdef ENABLE_BITTORRENT
SharedHandle<DefaultPieceStorage> ps
(new DefaultPieceStorage(downloadContext_, option_.get()));
diff --git a/src/SinkStreamFilter.cc b/src/SinkStreamFilter.cc
new file mode 100644
index 00000000..dadec822
--- /dev/null
+++ b/src/SinkStreamFilter.cc
@@ -0,0 +1,65 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "SinkStreamFilter.h"
+#include "BinaryStream.h"
+#include "Segment.h"
+
+namespace aria2 {
+
+const std::string SinkStreamFilter::NAME("SinkStreamFilter");
+
+SinkStreamFilter::SinkStreamFilter(bool hashUpdate):
+ hashUpdate_(hashUpdate),
+ bytesProcessed_(0) {}
+
+ssize_t SinkStreamFilter::transform
+(const SharedHandle<BinaryStream>& out,
+ const SharedHandle<Segment>& segment,
+ const unsigned char* inbuf, size_t inlen)
+{
+ if(inlen > 0) {
+ out->writeData(inbuf, inlen, segment->getPositionToWrite());
+#ifdef ENABLE_MESSAGE_DIGEST
+ if(hashUpdate_) {
+ segment->updateHash(segment->getWrittenLength(), inbuf, inlen);
+ }
+#endif // ENABLE_MESSAGE_DIGEST
+ segment->updateWrittenLength(inlen);
+ }
+ bytesProcessed_ = inlen;
+ return bytesProcessed_;
+}
+
+} // namespace aria2
diff --git a/src/SinkStreamFilter.h b/src/SinkStreamFilter.h
new file mode 100644
index 00000000..c8dcc76f
--- /dev/null
+++ b/src/SinkStreamFilter.h
@@ -0,0 +1,84 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_SINK_STREAM_FILTER_H
+#define D_SINK_STREAM_FILTER_H
+
+#include "StreamFilter.h"
+
+namespace aria2 {
+
+class SinkStreamFilter:public StreamFilter {
+private:
+ bool hashUpdate_;
+
+ size_t bytesProcessed_;
+public:
+ SinkStreamFilter(bool hashUpdate = false);
+
+ virtual void init() {}
+
+ virtual ssize_t transform
+ (const SharedHandle<BinaryStream>& out,
+ const SharedHandle<Segment>& segment,
+ const unsigned char* inbuf, size_t inlen);
+
+ virtual bool finished()
+ {
+ return true;
+ }
+
+ virtual void release() {}
+
+ virtual const std::string& getName() const
+ {
+ return NAME;
+ }
+
+ static const std::string NAME;
+
+ virtual size_t getBytesProcessed() const
+ {
+ return bytesProcessed_;
+ }
+
+ virtual bool installDelegate(const SharedHandle<StreamFilter>& filter)
+ {
+ return false;
+ }
+};
+
+} // namespace aria2
+
+#endif // D_SINK_STREAM_FILTER_H
diff --git a/src/StreamFilter.cc b/src/StreamFilter.cc
new file mode 100644
index 00000000..e88fe943
--- /dev/null
+++ b/src/StreamFilter.cc
@@ -0,0 +1,53 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "StreamFilter.h"
+
+namespace aria2 {
+
+StreamFilter::StreamFilter
+(const SharedHandle<StreamFilter>& delegate):
+ delegate_(delegate) {}
+
+bool StreamFilter::installDelegate(const SharedHandle<StreamFilter>& filter)
+{
+ if(delegate_.isNull()) {
+ delegate_ = filter;
+ return true;
+ } else {
+ return delegate_->installDelegate(filter);
+ }
+}
+
+} // namespace aria2
diff --git a/src/StreamFilter.h b/src/StreamFilter.h
new file mode 100644
index 00000000..f1588976
--- /dev/null
+++ b/src/StreamFilter.h
@@ -0,0 +1,84 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_STREAM_FILTER_H
+#define D_STREAM_FILTER_H
+
+#include "common.h"
+#include <string>
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class BinaryStream;
+class Segment;
+
+// Interface for basic decoding functionality.
+class StreamFilter {
+private:
+ SharedHandle<StreamFilter> delegate_;
+public:
+ StreamFilter
+ (const SharedHandle<StreamFilter>& delegate = SharedHandle<StreamFilter>());
+
+ virtual ~StreamFilter() {}
+
+ // init() must be called before calling decode().
+ virtual void init() = 0;
+
+ virtual ssize_t transform(const SharedHandle<BinaryStream>& out,
+ const SharedHandle<Segment>& segment,
+ const unsigned char* inbuf, size_t inlen) = 0;
+
+ virtual bool finished() = 0;
+
+ // The call of release() will free allocated resources.
+ // After calling release(), the object can be reused by calling init().
+ virtual void release() = 0;
+
+ virtual const std::string& getName() const = 0;
+
+ virtual size_t getBytesProcessed() const = 0;
+
+ virtual bool installDelegate(const SharedHandle<StreamFilter>& filter);
+
+ SharedHandle<StreamFilter> getDelegate() const
+ {
+ return delegate_;
+ }
+};
+
+} // namespace aria2
+
+#endif // D_STREAM_FILTER_H