|
@@ -32,6 +32,7 @@
|
|
|
* files in the program, then also delete it here.
|
|
|
*/
|
|
|
/* copyright --> */
|
|
|
+
|
|
|
#include "HttpResponseCommand.h"
|
|
|
#include "DownloadEngine.h"
|
|
|
#include "DownloadContext.h"
|
|
@@ -84,38 +85,37 @@
|
|
|
namespace aria2 {
|
|
|
|
|
|
namespace {
|
|
|
-std::unique_ptr<StreamFilter> getTransferEncodingStreamFilter
|
|
|
-(HttpResponse* httpResponse,
|
|
|
- std::unique_ptr<StreamFilter> delegate = nullptr)
|
|
|
+
|
|
|
+std::unique_ptr<StreamFilter> getTransferEncodingStreamFilter(
|
|
|
+ HttpResponse* httpResponse,
|
|
|
+ std::unique_ptr<StreamFilter> delegate = nullptr)
|
|
|
{
|
|
|
- if(httpResponse->isTransferEncodingSpecified()) {
|
|
|
+ if (httpResponse->isTransferEncodingSpecified()) {
|
|
|
auto filter = httpResponse->getTransferEncodingStreamFilter();
|
|
|
- if(!filter) {
|
|
|
- throw DL_ABORT_EX
|
|
|
- (fmt(EX_TRANSFER_ENCODING_NOT_SUPPORTED,
|
|
|
- httpResponse->getTransferEncoding().c_str()));
|
|
|
+ if (!filter) {
|
|
|
+ throw DL_ABORT_EX(fmt(EX_TRANSFER_ENCODING_NOT_SUPPORTED,
|
|
|
+ httpResponse->getTransferEncoding().c_str()));
|
|
|
}
|
|
|
filter->init();
|
|
|
filter->installDelegate(std::move(delegate));
|
|
|
return filter;
|
|
|
}
|
|
|
+
|
|
|
return delegate;
|
|
|
}
|
|
|
-} // namespace
|
|
|
|
|
|
-namespace {
|
|
|
-std::unique_ptr<StreamFilter> getContentEncodingStreamFilter
|
|
|
-(HttpResponse* httpResponse,
|
|
|
- std::unique_ptr<StreamFilter> delegate = nullptr)
|
|
|
+std::unique_ptr<StreamFilter> getContentEncodingStreamFilter(
|
|
|
+ HttpResponse* httpResponse,
|
|
|
+ std::unique_ptr<StreamFilter> delegate = nullptr)
|
|
|
{
|
|
|
- if(httpResponse->isContentEncodingSpecified()) {
|
|
|
+ if (httpResponse->isContentEncodingSpecified()) {
|
|
|
auto filter = httpResponse->getContentEncodingStreamFilter();
|
|
|
- if(!filter) {
|
|
|
- A2_LOG_INFO
|
|
|
- (fmt("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()));
|
|
|
+ if (!filter) {
|
|
|
+ A2_LOG_INFO(fmt("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()));
|
|
|
}
|
|
|
filter->init();
|
|
|
filter->installDelegate(std::move(delegate));
|
|
@@ -123,17 +123,19 @@ std::unique_ptr<StreamFilter> getContentEncodingStreamFilter
|
|
|
}
|
|
|
return delegate;
|
|
|
}
|
|
|
+
|
|
|
} // namespace
|
|
|
|
|
|
-HttpResponseCommand::HttpResponseCommand
|
|
|
-(cuid_t cuid,
|
|
|
- const std::shared_ptr<Request>& req,
|
|
|
- const std::shared_ptr<FileEntry>& fileEntry,
|
|
|
- RequestGroup* requestGroup,
|
|
|
- const std::shared_ptr<HttpConnection>& httpConnection,
|
|
|
- DownloadEngine* e,
|
|
|
- const std::shared_ptr<SocketCore>& s)
|
|
|
- : AbstractCommand(cuid, req, fileEntry, requestGroup, e, s,
|
|
|
+HttpResponseCommand::HttpResponseCommand(
|
|
|
+ cuid_t cuid,
|
|
|
+ const std::shared_ptr<Request>& req,
|
|
|
+ const std::shared_ptr<FileEntry>& fileEntry,
|
|
|
+ RequestGroup* requestGroup,
|
|
|
+ const std::shared_ptr<HttpConnection>& httpConnection,
|
|
|
+ DownloadEngine* e,
|
|
|
+ const std::shared_ptr<SocketCore>& s)
|
|
|
+ :
|
|
|
+ AbstractCommand(cuid, req, fileEntry, requestGroup, e, s,
|
|
|
httpConnection->getSocketRecvBuffer()),
|
|
|
httpConnection_(httpConnection)
|
|
|
{
|
|
@@ -145,7 +147,7 @@ HttpResponseCommand::~HttpResponseCommand() {}
|
|
|
bool HttpResponseCommand::executeInternal()
|
|
|
{
|
|
|
auto httpResponse = httpConnection_->receiveResponse();
|
|
|
- if(!httpResponse) {
|
|
|
+ if (!httpResponse) {
|
|
|
// The server has not responded to our request yet.
|
|
|
// For socket->wantRead() == true, setReadCheckSocket(socket) is already
|
|
|
// done in the constructor.
|
|
@@ -153,7 +155,8 @@ bool HttpResponseCommand::executeInternal()
|
|
|
addCommandSelf();
|
|
|
return false;
|
|
|
}
|
|
|
- // check HTTP status number
|
|
|
+
|
|
|
+ // check HTTP status code
|
|
|
httpResponse->validateResponse();
|
|
|
httpResponse->retrieveCookie();
|
|
|
|
|
@@ -161,168 +164,175 @@ bool HttpResponseCommand::executeInternal()
|
|
|
// Disable persistent connection if:
|
|
|
// Connection: close is received or the remote server is not HTTP/1.1.
|
|
|
// We don't care whether non-HTTP/1.1 server returns Connection: keep-alive.
|
|
|
- getRequest()->supportsPersistentConnection
|
|
|
- (httpResponse->supportsPersistentConnection());
|
|
|
- if(getRequest()->isPipeliningEnabled()) {
|
|
|
- getRequest()->setMaxPipelinedRequest
|
|
|
- (getOption()->getAsInt(PREF_MAX_HTTP_PIPELINING));
|
|
|
- } else {
|
|
|
- getRequest()->setMaxPipelinedRequest(1);
|
|
|
+ auto& req = getRequest();
|
|
|
+ req->supportsPersistentConnection(
|
|
|
+ httpResponse->supportsPersistentConnection());
|
|
|
+ if (req->isPipeliningEnabled()) {
|
|
|
+ req->setMaxPipelinedRequest(
|
|
|
+ getOption()->getAsInt(PREF_MAX_HTTP_PIPELINING));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ req->setMaxPipelinedRequest(1);
|
|
|
}
|
|
|
|
|
|
- int statusCode = httpResponse->getStatusCode();
|
|
|
+ auto statusCode = httpResponse->getStatusCode();
|
|
|
+ auto& ctx = getDownloadContext();
|
|
|
+ auto grp = getRequestGroup();
|
|
|
+ auto& fe = getFileEntry();
|
|
|
|
|
|
- if(statusCode == 304) {
|
|
|
+ if (statusCode == 304) {
|
|
|
int64_t totalLength = httpResponse->getEntityLength();
|
|
|
- getFileEntry()->setLength(totalLength);
|
|
|
- getRequestGroup()->initPieceStorage();
|
|
|
+ fe->setLength(totalLength);
|
|
|
+ grp->initPieceStorage();
|
|
|
getPieceStorage()->markAllPiecesDone();
|
|
|
// Just set checksum verification done.
|
|
|
- getDownloadContext()->setChecksumVerified(true);
|
|
|
- A2_LOG_NOTICE
|
|
|
- (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
|
|
|
- GroupId::toHex(getRequestGroup()->getGID()).c_str(),
|
|
|
- getRequestGroup()->getFirstFilePath().c_str()));
|
|
|
+ ctx->setChecksumVerified(true);
|
|
|
+ A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
|
|
|
+ GroupId::toHex(grp->getGID()).c_str(),
|
|
|
+ grp->getFirstFilePath().c_str()));
|
|
|
poolConnection();
|
|
|
- getFileEntry()->poolRequest(getRequest());
|
|
|
+ fe->poolRequest(req);
|
|
|
return true;
|
|
|
}
|
|
|
- if(!getPieceStorage()) {
|
|
|
+
|
|
|
+ if (!getPieceStorage()) {
|
|
|
// Metalink/HTTP
|
|
|
- if(getDownloadContext()->getAcceptMetalink()) {
|
|
|
- if(httpHeader->defined(HttpHeader::LINK)) {
|
|
|
- getDownloadContext()->setAcceptMetalink(false);
|
|
|
+ if (ctx->getAcceptMetalink()) {
|
|
|
+ if (httpHeader->defined(HttpHeader::LINK)) {
|
|
|
+ ctx->setAcceptMetalink(false);
|
|
|
std::vector<MetalinkHttpEntry> entries;
|
|
|
httpResponse->getMetalinKHttpEntries(entries, getOption());
|
|
|
for(const auto& e : entries) {
|
|
|
- getFileEntry()->addUri(e.uri);
|
|
|
+ fe->addUri(e.uri);
|
|
|
A2_LOG_DEBUG(fmt("Adding URI=%s", e.uri.c_str()));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
#ifdef ENABLE_MESSAGE_DIGEST
|
|
|
- if(httpHeader->defined(HttpHeader::DIGEST)) {
|
|
|
+ if (httpHeader->defined(HttpHeader::DIGEST)) {
|
|
|
std::vector<Checksum> checksums;
|
|
|
httpResponse->getDigest(checksums);
|
|
|
for(const auto &checksum : checksums) {
|
|
|
- if(getDownloadContext()->getHashType().empty()) {
|
|
|
+ if (ctx->getHashType().empty()) {
|
|
|
A2_LOG_DEBUG(fmt("Setting digest: type=%s, digest=%s",
|
|
|
checksum.getHashType().c_str(),
|
|
|
checksum.getDigest().c_str()));
|
|
|
- getDownloadContext()->setDigest(checksum.getHashType(),
|
|
|
- checksum.getDigest());
|
|
|
+ ctx->setDigest(checksum.getHashType(), checksum.getDigest());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (checkChecksum(ctx, checksum)) {
|
|
|
break;
|
|
|
- } else {
|
|
|
- if(checkChecksum(getDownloadContext(), checksum)) {
|
|
|
- break;
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
#endif // ENABLE_MESSAGE_DIGEST
|
|
|
}
|
|
|
- if(statusCode >= 300) {
|
|
|
- if(statusCode == 404) {
|
|
|
- getRequestGroup()->increaseAndValidateFileNotFoundCount();
|
|
|
+
|
|
|
+ if (statusCode >= 300) {
|
|
|
+ if (statusCode == 404) {
|
|
|
+ grp->increaseAndValidateFileNotFoundCount();
|
|
|
}
|
|
|
return skipResponseBody(std::move(httpResponse));
|
|
|
}
|
|
|
- if(getFileEntry()->isUniqueProtocol()) {
|
|
|
+
|
|
|
+ if (fe->isUniqueProtocol()) {
|
|
|
// Redirection should be considered here. We need to parse
|
|
|
// original URI to get hostname.
|
|
|
const std::string& uri = getRequest()->getUri();
|
|
|
uri_split_result us;
|
|
|
- if(uri_split(&us, uri.c_str()) == 0) {
|
|
|
+ if (uri_split(&us, uri.c_str()) == 0) {
|
|
|
std::string host = uri::getFieldString(us, USR_HOST, uri.c_str());
|
|
|
- getFileEntry()->removeURIWhoseHostnameIs(host);
|
|
|
+ fe->removeURIWhoseHostnameIs(host);
|
|
|
}
|
|
|
}
|
|
|
- if(!getPieceStorage()) {
|
|
|
- getDownloadContext()->setAcceptMetalink(false);
|
|
|
+
|
|
|
+ if (!getPieceStorage()) {
|
|
|
+ ctx->setAcceptMetalink(false);
|
|
|
int64_t totalLength = httpResponse->getEntityLength();
|
|
|
- getFileEntry()->setLength(totalLength);
|
|
|
- if(getFileEntry()->getPath().empty()) {
|
|
|
- getFileEntry()->setPath
|
|
|
- (util::createSafePath
|
|
|
- (getOption()->get(PREF_DIR), httpResponse->determinFilename()));
|
|
|
+ fe->setLength(totalLength);
|
|
|
+ if (fe->getPath().empty()) {
|
|
|
+ fe->setPath(util::createSafePath(getOption()->get(PREF_DIR),
|
|
|
+ httpResponse->determinFilename()));
|
|
|
}
|
|
|
- getFileEntry()->setContentType(httpResponse->getContentType());
|
|
|
- getRequestGroup()->preDownloadProcessing();
|
|
|
- if(getDownloadEngine()->getRequestGroupMan()->
|
|
|
- isSameFileBeingDownloaded(getRequestGroup())) {
|
|
|
- throw DOWNLOAD_FAILURE_EXCEPTION2
|
|
|
- (fmt(EX_DUPLICATE_FILE_DOWNLOAD,
|
|
|
- getRequestGroup()->getFirstFilePath().c_str()),
|
|
|
- error_code::DUPLICATE_DOWNLOAD);
|
|
|
+ fe->setContentType(httpResponse->getContentType());
|
|
|
+ grp->preDownloadProcessing();
|
|
|
+ if (getDownloadEngine()->getRequestGroupMan()->isSameFileBeingDownloaded(grp)) {
|
|
|
+ throw DOWNLOAD_FAILURE_EXCEPTION2(fmt(EX_DUPLICATE_FILE_DOWNLOAD,
|
|
|
+ grp->getFirstFilePath().c_str()),
|
|
|
+ error_code::DUPLICATE_DOWNLOAD);
|
|
|
}
|
|
|
+
|
|
|
// update last modified time
|
|
|
updateLastModifiedTime(httpResponse->getLastModifiedTime());
|
|
|
|
|
|
// If both transfer-encoding and total length is specified, we
|
|
|
// assume we can do segmented downloading
|
|
|
- if(totalLength == 0 || shouldInflateContentEncoding(httpResponse.get())) {
|
|
|
+ if (totalLength == 0 || shouldInflateContentEncoding(httpResponse.get())) {
|
|
|
// we ignore content-length when inflate is required
|
|
|
- getFileEntry()->setLength(0);
|
|
|
- if(getRequest()->getMethod() == Request::METHOD_GET &&
|
|
|
- (totalLength != 0 ||
|
|
|
- !httpResponse->getHttpHeader()->defined(HttpHeader::CONTENT_LENGTH))){
|
|
|
+ fe->setLength(0);
|
|
|
+ if (req->getMethod() == Request::METHOD_GET && (totalLength != 0 ||
|
|
|
+ !httpResponse->getHttpHeader()->defined(HttpHeader::CONTENT_LENGTH))){
|
|
|
// DownloadContext::knowsTotalLength() == true only when
|
|
|
// server says the size of file is 0 explicitly.
|
|
|
getDownloadContext()->markTotalLengthIsUnknown();
|
|
|
}
|
|
|
return handleOtherEncoding(std::move(httpResponse));
|
|
|
- } else {
|
|
|
- return handleDefaultEncoding(std::move(httpResponse));
|
|
|
}
|
|
|
- } else {
|
|
|
+
|
|
|
+ return handleDefaultEncoding(std::move(httpResponse));
|
|
|
+ }
|
|
|
+
|
|
|
#ifdef ENABLE_MESSAGE_DIGEST
|
|
|
- if(!getDownloadContext()->getHashType().empty() &&
|
|
|
- httpHeader->defined(HttpHeader::DIGEST)) {
|
|
|
- std::vector<Checksum> checksums;
|
|
|
- httpResponse->getDigest(checksums);
|
|
|
- for(const auto &checksum : checksums) {
|
|
|
- if(checkChecksum(getDownloadContext(), checksum)) {
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (!ctx->getHashType().empty() && httpHeader->defined(HttpHeader::DIGEST)) {
|
|
|
+ std::vector<Checksum> checksums;
|
|
|
+ httpResponse->getDigest(checksums);
|
|
|
+ for (const auto &checksum : checksums) {
|
|
|
+ if (checkChecksum(ctx, checksum)) {
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
#endif // ENABLE_MESSAGE_DIGEST
|
|
|
- // validate totalsize
|
|
|
- getRequestGroup()->validateTotalLength(getFileEntry()->getLength(),
|
|
|
- httpResponse->getEntityLength());
|
|
|
- // update last modified time
|
|
|
- updateLastModifiedTime(httpResponse->getLastModifiedTime());
|
|
|
- if(getRequestGroup()->getTotalLength() == 0) {
|
|
|
- // Since total length is unknown, the file size in previously
|
|
|
- // failed download could be larger than the size this time.
|
|
|
- // Also we can't resume in this case too. So truncate the file
|
|
|
- // anyway.
|
|
|
- getPieceStorage()->getDiskAdaptor()->truncate(0);
|
|
|
- auto teFilter = getTransferEncodingStreamFilter
|
|
|
- (httpResponse.get(),
|
|
|
- getContentEncodingStreamFilter(httpResponse.get()));
|
|
|
- getDownloadEngine()->addCommand
|
|
|
- (createHttpDownloadCommand(std::move(httpResponse),
|
|
|
- std::move(teFilter)));
|
|
|
- } else {
|
|
|
- auto teFilter = getTransferEncodingStreamFilter(httpResponse.get());
|
|
|
- getDownloadEngine()->addCommand
|
|
|
- (createHttpDownloadCommand(std::move(httpResponse),
|
|
|
- std::move(teFilter)));
|
|
|
- }
|
|
|
- return true;
|
|
|
+
|
|
|
+ // validate totalsize
|
|
|
+ grp->validateTotalLength(fe->getLength(), httpResponse->getEntityLength());
|
|
|
+ // update last modified time
|
|
|
+ updateLastModifiedTime(httpResponse->getLastModifiedTime());
|
|
|
+
|
|
|
+ if (grp->getTotalLength() == 0) {
|
|
|
+ // Since total length is unknown, the file size in previously
|
|
|
+ // failed download could be larger than the size this time.
|
|
|
+ // Also we can't resume in this case too. So truncate the file
|
|
|
+ // anyway.
|
|
|
+ getPieceStorage()->getDiskAdaptor()->truncate(0);
|
|
|
+ auto teFilter = getTransferEncodingStreamFilter(
|
|
|
+ httpResponse.get(),
|
|
|
+ getContentEncodingStreamFilter(httpResponse.get()));
|
|
|
+ getDownloadEngine()->addCommand(
|
|
|
+ createHttpDownloadCommand(std::move(httpResponse),
|
|
|
+ std::move(teFilter)));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ auto teFilter = getTransferEncodingStreamFilter(httpResponse.get());
|
|
|
+ getDownloadEngine()->addCommand(
|
|
|
+ createHttpDownloadCommand(std::move(httpResponse),
|
|
|
+ std::move(teFilter)));
|
|
|
}
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
void HttpResponseCommand::updateLastModifiedTime(const Time& lastModified)
|
|
|
{
|
|
|
- if(getOption()->getAsBool(PREF_REMOTE_TIME)) {
|
|
|
+ if (getOption()->getAsBool(PREF_REMOTE_TIME)) {
|
|
|
getRequestGroup()->updateLastModifiedTime(lastModified);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-bool HttpResponseCommand::shouldInflateContentEncoding
|
|
|
-(HttpResponse* httpResponse)
|
|
|
+bool HttpResponseCommand::shouldInflateContentEncoding(
|
|
|
+ HttpResponse* httpResponse)
|
|
|
{
|
|
|
// Basically, on the fly inflation cannot be made with segment
|
|
|
// download, because in each segment we don't know where the date
|
|
@@ -336,11 +346,11 @@ bool HttpResponseCommand::shouldInflateContentEncoding
|
|
|
(ce == "gzip" || ce == "deflate");
|
|
|
}
|
|
|
|
|
|
-bool HttpResponseCommand::handleDefaultEncoding
|
|
|
-(std::unique_ptr<HttpResponse> httpResponse)
|
|
|
+bool HttpResponseCommand::handleDefaultEncoding(
|
|
|
+ std::unique_ptr<HttpResponse> httpResponse)
|
|
|
{
|
|
|
- auto progressInfoFile = std::make_shared<DefaultBtProgressInfoFile>
|
|
|
- (getDownloadContext(), std::shared_ptr<PieceStorage>{}, getOption().get());
|
|
|
+ auto progressInfoFile = std::make_shared<DefaultBtProgressInfoFile>(
|
|
|
+ getDownloadContext(), std::shared_ptr<PieceStorage>{}, getOption().get());
|
|
|
getRequestGroup()->adjustFilename(progressInfoFile);
|
|
|
getRequestGroup()->initPieceStorage();
|
|
|
|
|
@@ -350,9 +360,10 @@ bool HttpResponseCommand::handleDefaultEncoding
|
|
|
}
|
|
|
|
|
|
auto checkEntry = getRequestGroup()->createCheckIntegrityEntry();
|
|
|
- if(!checkEntry) {
|
|
|
+ if (!checkEntry) {
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
File file(getRequestGroup()->getFirstFilePath());
|
|
|
// We have to make sure that command that has Request object must
|
|
|
// have segment after PieceStorage is initialized. See
|
|
@@ -363,37 +374,40 @@ bool HttpResponseCommand::handleDefaultEncoding
|
|
|
// we can't continue to use this socket because server sends all entity
|
|
|
// body instead of a segment.
|
|
|
// Therefore, we shutdown the socket here if pipelining is enabled.
|
|
|
- if(getRequest()->getMethod() == Request::METHOD_GET &&
|
|
|
+ if (getRequest()->getMethod() == Request::METHOD_GET &&
|
|
|
segment && segment->getPositionToWrite() == 0 &&
|
|
|
!getRequest()->isPipeliningEnabled()) {
|
|
|
auto teFilter = getTransferEncodingStreamFilter(httpResponse.get());
|
|
|
- checkEntry->pushNextCommand
|
|
|
- (createHttpDownloadCommand(std::move(httpResponse),
|
|
|
- std::move(teFilter)));
|
|
|
- } else {
|
|
|
+ checkEntry->pushNextCommand(
|
|
|
+ createHttpDownloadCommand(std::move(httpResponse),
|
|
|
+ std::move(teFilter)));
|
|
|
+ }
|
|
|
+ else {
|
|
|
getSegmentMan()->cancelSegment(getCuid());
|
|
|
getFileEntry()->poolRequest(getRequest());
|
|
|
}
|
|
|
|
|
|
prepareForNextAction(std::move(checkEntry));
|
|
|
|
|
|
- if(getRequest()->getMethod() == Request::METHOD_HEAD) {
|
|
|
+ if (getRequest()->getMethod() == Request::METHOD_HEAD) {
|
|
|
poolConnection();
|
|
|
getRequest()->setMethod(Request::METHOD_GET);
|
|
|
}
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-bool HttpResponseCommand::handleOtherEncoding
|
|
|
-(std::unique_ptr<HttpResponse> httpResponse) {
|
|
|
+bool HttpResponseCommand::handleOtherEncoding(
|
|
|
+ std::unique_ptr<HttpResponse> httpResponse)
|
|
|
+{
|
|
|
// We assume that RequestGroup::getTotalLength() == 0 here
|
|
|
- if(getOption()->getAsBool(PREF_DRY_RUN)) {
|
|
|
+ if (getOption()->getAsBool(PREF_DRY_RUN)) {
|
|
|
getRequestGroup()->initPieceStorage();
|
|
|
onDryRunFileFound();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- if(getRequest()->getMethod() == Request::METHOD_HEAD) {
|
|
|
+ if (getRequest()->getMethod() == Request::METHOD_HEAD) {
|
|
|
poolConnection();
|
|
|
getRequest()->setMethod(Request::METHOD_GET);
|
|
|
return prepareForRetry(0);
|
|
@@ -402,38 +416,37 @@ bool HttpResponseCommand::handleOtherEncoding
|
|
|
// In this context, knowsTotalLength() is true only when the file is
|
|
|
// really zero-length.
|
|
|
|
|
|
- auto streamFilter = getTransferEncodingStreamFilter
|
|
|
- (httpResponse.get(), getContentEncodingStreamFilter(httpResponse.get()));
|
|
|
+ auto streamFilter = getTransferEncodingStreamFilter(
|
|
|
+ httpResponse.get(), getContentEncodingStreamFilter(httpResponse.get()));
|
|
|
// If chunked transfer-encoding is specified, we have to read end of
|
|
|
// chunk markers(0\r\n\r\n, for example).
|
|
|
bool chunkedUsed = streamFilter &&
|
|
|
streamFilter->getName() == ChunkedDecodingStreamFilter::NAME;
|
|
|
|
|
|
// For zero-length file, check existing file comparing its size
|
|
|
- if(!chunkedUsed && getDownloadContext()->knowsTotalLength() &&
|
|
|
- getRequestGroup()->downloadFinishedByFileLength()) {
|
|
|
+ if (!chunkedUsed && getDownloadContext()->knowsTotalLength() &&
|
|
|
+ getRequestGroup()->downloadFinishedByFileLength()) {
|
|
|
getRequestGroup()->initPieceStorage();
|
|
|
+
|
|
|
#ifdef ENABLE_MESSAGE_DIGEST
|
|
|
// TODO Known issue: if .aria2 file exists, it will not be deleted
|
|
|
// on successful verification, because .aria2 file is not loaded.
|
|
|
// See also FtpNegotiationCommand::onFileSizeDetermined()
|
|
|
if(getDownloadContext()->isChecksumVerificationNeeded()) {
|
|
|
A2_LOG_DEBUG("Zero length file exists. Verify checksum.");
|
|
|
- auto entry = make_unique<ChecksumCheckIntegrityEntry>
|
|
|
- (getRequestGroup());
|
|
|
+ auto entry = make_unique<ChecksumCheckIntegrityEntry>(getRequestGroup());
|
|
|
entry->initValidator();
|
|
|
getPieceStorage()->getDiskAdaptor()->openExistingFile();
|
|
|
getDownloadEngine()->getCheckIntegrityMan()->pushEntry(std::move(entry));
|
|
|
} else
|
|
|
#endif // ENABLE_MESSAGE_DIGEST
|
|
|
- {
|
|
|
- getPieceStorage()->markAllPiecesDone();
|
|
|
- getDownloadContext()->setChecksumVerified(true);
|
|
|
- A2_LOG_NOTICE
|
|
|
- (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
|
|
|
- GroupId::toHex(getRequestGroup()->getGID()).c_str(),
|
|
|
- getRequestGroup()->getFirstFilePath().c_str()));
|
|
|
- }
|
|
|
+ {
|
|
|
+ getPieceStorage()->markAllPiecesDone();
|
|
|
+ getDownloadContext()->setChecksumVerified(true);
|
|
|
+ A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
|
|
|
+ GroupId::toHex(getRequestGroup()->getGID()).c_str(),
|
|
|
+ getRequestGroup()->getFirstFilePath().c_str()));
|
|
|
+ }
|
|
|
poolConnection();
|
|
|
return true;
|
|
|
}
|
|
@@ -445,55 +458,55 @@ bool HttpResponseCommand::handleOtherEncoding
|
|
|
// Local file size becomes zero when DiskAdaptor::initAndOpenFile()
|
|
|
// is called. So zero-length file is complete if chunked encoding is
|
|
|
// not used.
|
|
|
- if(!chunkedUsed && getDownloadContext()->knowsTotalLength()) {
|
|
|
+ if (!chunkedUsed && getDownloadContext()->knowsTotalLength()) {
|
|
|
A2_LOG_DEBUG("File length becomes zero and it means download completed.");
|
|
|
// TODO Known issue: if .aria2 file exists, it will not be deleted
|
|
|
// on successful verification, because .aria2 file is not loaded.
|
|
|
// See also FtpNegotiationCommand::onFileSizeDetermined()
|
|
|
#ifdef ENABLE_MESSAGE_DIGEST
|
|
|
- if(getDownloadContext()->isChecksumVerificationNeeded()) {
|
|
|
+ if (getDownloadContext()->isChecksumVerificationNeeded()) {
|
|
|
A2_LOG_DEBUG("Verify checksum for zero-length file");
|
|
|
- auto entry = make_unique<ChecksumCheckIntegrityEntry>
|
|
|
- (getRequestGroup());
|
|
|
+ auto entry = make_unique<ChecksumCheckIntegrityEntry>(getRequestGroup());
|
|
|
entry->initValidator();
|
|
|
getDownloadEngine()->getCheckIntegrityMan()->pushEntry(std::move(entry));
|
|
|
} else
|
|
|
#endif // ENABLE_MESSAGE_DIGEST
|
|
|
- {
|
|
|
- getRequestGroup()->getPieceStorage()->markAllPiecesDone();
|
|
|
- }
|
|
|
+ {
|
|
|
+ getRequestGroup()->getPieceStorage()->markAllPiecesDone();
|
|
|
+ }
|
|
|
poolConnection();
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
// We have to make sure that command that has Request object must
|
|
|
// have segment after PieceStorage is initialized. See
|
|
|
// AbstractCommand::execute()
|
|
|
getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
|
|
|
|
|
|
- getDownloadEngine()->addCommand
|
|
|
- (createHttpDownloadCommand(std::move(httpResponse),
|
|
|
- std::move(streamFilter)));
|
|
|
+ getDownloadEngine()->addCommand(
|
|
|
+ createHttpDownloadCommand(std::move(httpResponse),
|
|
|
+ std::move(streamFilter)));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-bool HttpResponseCommand::skipResponseBody
|
|
|
-(std::unique_ptr<HttpResponse> httpResponse)
|
|
|
+bool HttpResponseCommand::skipResponseBody(
|
|
|
+ std::unique_ptr<HttpResponse> httpResponse)
|
|
|
{
|
|
|
auto filter = getTransferEncodingStreamFilter(httpResponse.get());
|
|
|
// We don't use Content-Encoding here because this response body is just
|
|
|
// thrown away.
|
|
|
auto httpResponsePtr = httpResponse.get();
|
|
|
- auto command = make_unique<HttpSkipResponseCommand>
|
|
|
- (getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
|
|
|
- httpConnection_, std::move(httpResponse),
|
|
|
- getDownloadEngine(), getSocket());
|
|
|
+ auto command = make_unique<HttpSkipResponseCommand>(
|
|
|
+ getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
|
|
|
+ httpConnection_, std::move(httpResponse), getDownloadEngine(),
|
|
|
+ getSocket());
|
|
|
command->installStreamFilter(std::move(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
|
|
|
- if(getRequest()->getMethod() == Request::METHOD_HEAD ||
|
|
|
- (httpResponsePtr->getEntityLength() == 0 &&
|
|
|
- !httpResponsePtr->isTransferEncodingSpecified())) {
|
|
|
+ if (getRequest()->getMethod() == Request::METHOD_HEAD ||
|
|
|
+ (httpResponsePtr->getEntityLength() == 0 &&
|
|
|
+ !httpResponsePtr->isTransferEncodingSpecified())) {
|
|
|
command->setStatusRealtime();
|
|
|
// If entity length == 0, then socket read/write check must be disabled.
|
|
|
command->disableSocketCheck();
|
|
@@ -505,50 +518,52 @@ bool HttpResponseCommand::skipResponseBody
|
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
+
|
|
|
bool decideFileAllocation(StreamFilter* filter)
|
|
|
{
|
|
|
#ifdef HAVE_ZLIB
|
|
|
- for(StreamFilter* f = filter; f; f = f->getDelegate().get()){
|
|
|
+ for (StreamFilter* f = filter; f; f = f->getDelegate().get()){
|
|
|
// 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) {
|
|
|
+ if (f->getName() == GZipDecodingStreamFilter::NAME) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
#endif // HAVE_ZLIB
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
} // namespace
|
|
|
|
|
|
std::unique_ptr<HttpDownloadCommand>
|
|
|
-HttpResponseCommand::createHttpDownloadCommand
|
|
|
-(std::unique_ptr<HttpResponse> httpResponse,
|
|
|
- std::unique_ptr<StreamFilter> filter)
|
|
|
+HttpResponseCommand::createHttpDownloadCommand(
|
|
|
+ std::unique_ptr<HttpResponse> httpResponse,
|
|
|
+ std::unique_ptr<StreamFilter> filter)
|
|
|
{
|
|
|
|
|
|
- auto command = make_unique<HttpDownloadCommand>
|
|
|
- (getCuid(), getRequest(), getFileEntry(),
|
|
|
- getRequestGroup(),
|
|
|
- std::move(httpResponse), httpConnection_,
|
|
|
- getDownloadEngine(), getSocket());
|
|
|
+ auto command = make_unique<HttpDownloadCommand>(
|
|
|
+ getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
|
|
|
+ std::move(httpResponse), httpConnection_, getDownloadEngine(),
|
|
|
+ getSocket());
|
|
|
command->setStartupIdleTime(getOption()->getAsInt(PREF_STARTUP_IDLE_TIME));
|
|
|
- command->setLowestDownloadSpeedLimit
|
|
|
- (getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
|
|
|
- if(getRequestGroup()->isFileAllocationEnabled() &&
|
|
|
- !decideFileAllocation(filter.get())) {
|
|
|
+ command->setLowestDownloadSpeedLimit(
|
|
|
+ getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
|
|
|
+ if (getRequestGroup()->isFileAllocationEnabled() &&
|
|
|
+ !decideFileAllocation(filter.get())) {
|
|
|
getRequestGroup()->setFileAllocationEnabled(false);
|
|
|
}
|
|
|
command->installStreamFilter(std::move(filter));
|
|
|
- getRequestGroup()->getURISelector()->tuneDownloadCommand
|
|
|
- (getFileEntry()->getRemainingUris(), command.get());
|
|
|
+ getRequestGroup()->getURISelector()->tuneDownloadCommand(
|
|
|
+ getFileEntry()->getRemainingUris(), command.get());
|
|
|
|
|
|
return std::move(command);
|
|
|
}
|
|
|
|
|
|
void HttpResponseCommand::poolConnection()
|
|
|
{
|
|
|
- if(getRequest()->supportsPersistentConnection()) {
|
|
|
+ if (getRequest()->supportsPersistentConnection()) {
|
|
|
getDownloadEngine()->poolSocket(getRequest(), createProxyRequest(),
|
|
|
getSocket());
|
|
|
}
|
|
@@ -562,18 +577,17 @@ void HttpResponseCommand::onDryRunFileFound()
|
|
|
}
|
|
|
|
|
|
#ifdef ENABLE_MESSAGE_DIGEST
|
|
|
-bool HttpResponseCommand::checkChecksum
|
|
|
-(const std::shared_ptr<DownloadContext>& dctx,
|
|
|
- const Checksum& checksum)
|
|
|
+bool HttpResponseCommand::checkChecksum(
|
|
|
+ const std::shared_ptr<DownloadContext>& dctx, const Checksum& checksum)
|
|
|
{
|
|
|
- if(dctx->getHashType() == checksum.getHashType()) {
|
|
|
- if(dctx->getDigest() == checksum.getDigest()) {
|
|
|
- A2_LOG_INFO("Valid hash found in Digest header field.");
|
|
|
- return true;
|
|
|
- } else {
|
|
|
+ if (dctx->getHashType() == checksum.getHashType()) {
|
|
|
+ if (dctx->getDigest() != checksum.getDigest()) {
|
|
|
throw DL_ABORT_EX("Invalid hash found in Digest header field.");
|
|
|
}
|
|
|
+ A2_LOG_INFO("Valid hash found in Digest header field.");
|
|
|
+ return true;
|
|
|
}
|
|
|
+
|
|
|
return false;
|
|
|
}
|
|
|
#endif // ENABLE_MESSAGE_DIGEST
|