/* */ #include "HttpResponse.h" #include "Request.h" #include "Segment.h" #include "HttpRequest.h" #include "HttpHeader.h" #include "Range.h" #include "LogFactory.h" #include "Logger.h" #include "util.h" #include "message.h" #include "DlAbortEx.h" #include "DlRetryEx.h" #include "fmt.h" #include "A2STR.h" #include "CookieStorage.h" #include "AuthConfigFactory.h" #include "AuthConfig.h" #include "ChunkedDecodingStreamFilter.h" #include "error_code.h" #ifdef HAVE_ZLIB # include "GZipDecodingStreamFilter.h" #endif // HAVE_ZLIB namespace aria2 { HttpResponse::HttpResponse() : cuid_(0) {} HttpResponse::~HttpResponse() {} void HttpResponse::validateResponse() const { int statusCode = getStatusCode(); if(statusCode >= 400) { return; } if(statusCode == 304) { if(!httpRequest_->conditionalRequest()) { throw DL_ABORT_EX2("Got 304 without If-Modified-Since or If-None-Match", error_code::HTTP_PROTOCOL_ERROR); } } else if(statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307) { if(!httpHeader_->defined(HttpHeader::LOCATION)) { throw DL_ABORT_EX2(fmt(EX_LOCATION_HEADER_REQUIRED, statusCode), error_code::HTTP_PROTOCOL_ERROR); } return; } else if(statusCode == 200 || statusCode == 206) { if(!httpHeader_->defined(HttpHeader::TRANSFER_ENCODING)) { // compare the received range against the requested range RangeHandle responseRange = httpHeader_->getRange(); if(!httpRequest_->isRangeSatisfied(responseRange)) { throw DL_ABORT_EX2 (fmt(EX_INVALID_RANGE_HEADER, util::itos(httpRequest_->getStartByte(), true).c_str(), util::itos(httpRequest_->getEndByte(), true).c_str(), util::uitos(httpRequest_->getEntityLength(), true).c_str(), util::itos(responseRange->getStartByte(), true).c_str(), util::itos(responseRange->getEndByte(), true).c_str(), util::uitos(responseRange->getEntityLength(), true).c_str()), error_code::CANNOT_RESUME); } } } else { throw DL_ABORT_EX2(fmt("Unexpected status %d", statusCode), error_code::HTTP_PROTOCOL_ERROR); } } std::string HttpResponse::determinFilename() const { std::string contentDisposition = util::getContentDispositionFilename (httpHeader_->getFirst(HttpHeader::CONTENT_DISPOSITION)); if(contentDisposition.empty()) { std::string file = util::percentDecode(httpRequest_->getFile()); if(file.empty()) { return "index.html"; } else { return file; } } else { A2_LOG_INFO(fmt(MSG_CONTENT_DISPOSITION_DETECTED, cuid_, contentDisposition.c_str())); return contentDisposition; } } void HttpResponse::retrieveCookie() { Time now; std::vector v = httpHeader_->get(HttpHeader::SET_COOKIE); for(std::vector::const_iterator itr = v.begin(), eoi = v.end(); itr != eoi; ++itr) { httpRequest_->getCookieStorage()->parseAndStore (*itr, httpRequest_->getHost(), httpRequest_->getDir(), now.getTime()); } } bool HttpResponse::isRedirect() const { int statusCode = getStatusCode(); return (301 == statusCode || 302 == statusCode || 303 == statusCode || 307 == statusCode) && httpHeader_->defined(HttpHeader::LOCATION); } void HttpResponse::processRedirect() { if(httpRequest_->getRequest()->redirectUri(getRedirectURI())) { A2_LOG_INFO(fmt(MSG_REDIRECT, cuid_, httpRequest_->getRequest()->getCurrentUri().c_str())); } else { throw DL_RETRY_EX (fmt("CUID#%lld - Redirect to %s failed. It may not be a valid URI.", cuid_, httpRequest_->getRequest()->getCurrentUri().c_str())); } } std::string HttpResponse::getRedirectURI() const { return httpHeader_->getFirst(HttpHeader::LOCATION); } bool HttpResponse::isTransferEncodingSpecified() const { return httpHeader_->defined(HttpHeader::TRANSFER_ENCODING); } std::string HttpResponse::getTransferEncoding() const { // TODO See TODO in getTransferEncodingStreamFilter() return httpHeader_->getFirst(HttpHeader::TRANSFER_ENCODING); } SharedHandle HttpResponse::getTransferEncodingStreamFilter() const { SharedHandle 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) { filter.reset(new ChunkedDecodingStreamFilter()); } } return filter; } bool HttpResponse::isContentEncodingSpecified() const { return httpHeader_->defined(HttpHeader::CONTENT_ENCODING); } const std::string& HttpResponse::getContentEncoding() const { return httpHeader_->getFirst(HttpHeader::CONTENT_ENCODING); } SharedHandle HttpResponse::getContentEncodingStreamFilter() const { SharedHandle filter; #ifdef HAVE_ZLIB if(getContentEncoding() == HttpHeader::GZIP || getContentEncoding() == HttpHeader::DEFLATE) { filter.reset(new GZipDecodingStreamFilter()); } #endif // HAVE_ZLIB return filter; } uint64_t HttpResponse::getContentLength() const { if(!httpHeader_) { return 0; } else { return httpHeader_->getRange()->getContentLength(); } } uint64_t HttpResponse::getEntityLength() const { if(!httpHeader_) { return 0; } else { return httpHeader_->getRange()->getEntityLength(); } } std::string HttpResponse::getContentType() const { if(!httpHeader_) { return A2STR::NIL; } else { std::pair p; util::divide(p, httpHeader_->getFirst(HttpHeader::CONTENT_TYPE), ';'); return p.first; } } void HttpResponse::setHttpHeader(const SharedHandle& httpHeader) { httpHeader_ = httpHeader; } void HttpResponse::setHttpRequest(const SharedHandle& httpRequest) { httpRequest_ = httpRequest; } int HttpResponse::getStatusCode() const { return httpHeader_->getStatusCode(); } bool HttpResponse::hasRetryAfter() const { return httpHeader_->defined(HttpHeader::RETRY_AFTER); } time_t HttpResponse::getRetryAfter() const { return httpHeader_->getFirstAsUInt(HttpHeader::RETRY_AFTER); } Time HttpResponse::getLastModifiedTime() const { return Time::parseHTTPDate(httpHeader_->getFirst(HttpHeader::LAST_MODIFIED)); } bool HttpResponse::supportsPersistentConnection() const { std::string connection = util::toLower(httpHeader_->getFirst(HttpHeader::CONNECTION)); std::string version = httpHeader_->getVersion(); return connection.find(HttpHeader::CLOSE) == std::string::npos && (version == HttpHeader::HTTP_1_1 || connection.find("keep-alive") != std::string::npos) && (!httpRequest_->isProxyRequestSet() || util::toLower(httpHeader_->getFirst("Proxy-Connection")).find("keep-alive") != std::string::npos); } } // namespace aria2