/* */ #include "HttpSkipResponseCommand.h" #include "HttpConnection.h" #include "HttpResponse.h" #include "message.h" #include "SocketCore.h" #include "DlRetryEx.h" #include "Request.h" #include "DownloadEngine.h" #include "Logger.h" #include "LogFactory.h" #include "HttpRequest.h" #include "Segment.h" #include "util.h" #include "fmt.h" #include "DlAbortEx.h" #include "HttpHeader.h" #include "prefs.h" #include "Option.h" #include "CookieStorage.h" #include "AuthConfigFactory.h" #include "AuthConfig.h" #include "DownloadContext.h" #include "StreamFilter.h" #include "BinaryStream.h" #include "NullSinkStreamFilter.h" #include "SinkStreamFilter.h" #include "error_code.h" namespace aria2 { HttpSkipResponseCommand::HttpSkipResponseCommand (cuid_t cuid, const SharedHandle& req, const SharedHandle& fileEntry, RequestGroup* requestGroup, const SharedHandle& httpConnection, const SharedHandle& httpResponse, DownloadEngine* e, const SharedHandle& s) : 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::installStreamFilter (const SharedHandle& streamFilter) { if(!streamFilter) { return; } streamFilter->installDelegate(streamFilter_); streamFilter_ = streamFilter; sinkFilterOnly_ = util::endsWith(streamFilter_->getName(), SinkStreamFilter::NAME); } bool HttpSkipResponseCommand::executeInternal() { if(getRequest()->getMethod() == Request::METHOD_HEAD || (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. // In this case, the content is thrown away and socket cannot be pooled. if(getRequest()->getMethod() == Request::METHOD_HEAD || httpResponse_->getHttpHeader()->defined(HttpHeader::CONTENT_LENGTH)) { poolConnection(); } return processResponse(); } const size_t BUFSIZE = 16*1024; unsigned char buf[BUFSIZE]; size_t bufSize; if(sinkFilterOnly_ && totalLength_ > 0) { bufSize = totalLength_-receivedBytes_; } else { bufSize = BUFSIZE; } try { 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. streamFilter_->transform(SharedHandle(), SharedHandle(), buf, bufSize); bufSize = streamFilter_->getBytesProcessed(); getSocket()->readData(buf, bufSize); } if(totalLength_ != 0 && bufSize == 0 && !getSocket()->wantRead() && !getSocket()->wantWrite()) { throw DL_RETRY_EX(EX_GOT_EOF); } } catch(RecoverableException& e) { A2_LOG_DEBUG_EX(EX_EXCEPTION_CAUGHT, e) return processResponse(); } bool finished = false; if(sinkFilterOnly_) { if(bufSize == 0) { if(!getSocket()->wantRead() && !getSocket()->wantWrite()) { return processResponse(); } } else { finished = (totalLength_ == receivedBytes_); } } else { finished = streamFilter_->finished(); } if(finished) { poolConnection(); return processResponse(); } else { setWriteCheckSocketIf(getSocket(), getSocket()->wantWrite()); getDownloadEngine()->addCommand(this); return false; } } void HttpSkipResponseCommand::poolConnection() const { if(getRequest()->supportsPersistentConnection()) { getDownloadEngine()->poolSocket (getRequest(), createProxyRequest(), getSocket()); } } bool HttpSkipResponseCommand::processResponse() { int statusCode; if(httpResponse_->isRedirect()) { unsigned int rnum = httpResponse_->getHttpRequest()->getRequest()->getRedirectCount(); if(rnum >= Request::MAX_REDIRECT) { throw DL_ABORT_EX2(fmt("Too many redirects: count=%u", rnum), error_code::HTTP_TOO_MANY_REDIRECTS); } httpResponse_->processRedirect(); return prepareForRetry(0); } else if((statusCode = httpResponse_->getStatusCode()) >= 400) { if(statusCode == 401) { if(getOption()->getAsBool(PREF_HTTP_AUTH_CHALLENGE) && !httpResponse_->getHttpRequest()->authenticationUsed() && getDownloadEngine()->getAuthConfigFactory()->activateBasicCred (getRequest()->getHost(), getRequest()->getDir(), getOption().get())) { return prepareForRetry(0); } else { throw DL_ABORT_EX2(EX_AUTH_FAILED, error_code::HTTP_AUTH_FAILED); } } else if(statusCode == 404) { throw DL_ABORT_EX2(MSG_RESOURCE_NOT_FOUND, error_code::RESOURCE_NOT_FOUND); } else { throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, statusCode), error_code::HTTP_PROTOCOL_ERROR); } } else { return prepareForRetry(0); } } void HttpSkipResponseCommand::disableSocketCheck() { disableReadCheckSocket(); disableWriteCheckSocket(); } } // namespace aria2