/* */ #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" #include "prefs.h" #include "Option.h" #include "Checksum.h" #include "uri.h" #include "MetalinkHttpEntry.h" #include "Base64.h" #ifdef ENABLE_MESSAGE_DIGEST #include "MessageDigest.h" #endif // ENABLE_MESSAGE_DIGEST #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().begin(), httpRequest_->getFile().end()); 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::pair::const_iterator, std::multimap::const_iterator> r = httpHeader_->getIterator(HttpHeader::SET_COOKIE); for(; r.first != r.second; ++r.first) { httpRequest_->getCookieStorage()->parseAndStore ((*r.first).second, 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 (util::percentEncodeMini(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())); } } const std::string& HttpResponse::getRedirectURI() const { return httpHeader_->getFirst(HttpHeader::LOCATION); } bool HttpResponse::isTransferEncodingSpecified() const { return httpHeader_->defined(HttpHeader::TRANSFER_ENCODING); } const 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 { bool parseMetalinkHttpLink(MetalinkHttpEntry& result, const std::string& s) { std::string::const_iterator first = std::find(s.begin(), s.end(), '<'); if(first == s.end()) { return false; } std::string::const_iterator last = std::find(first, s.end(), '>'); if(last == s.end()) { return false; } std::pair p = util::stripIter(first+1, last); if(p.first == p.second) { return false; } else { result.uri.assign(p.first, p.second); } last = std::find(last, s.end(), ';'); if(last != s.end()) { ++last; } bool ok = false; while(1) { std::string name, value; std::pair r = util::nextParam(name, value, last, s.end(), ';'); last = r.first; if(!r.second) { break; } if(value.empty()) { if(name == "pref") { result.pref = true; } } else { if(name == "rel") { if(value == "duplicate") { ok = true; } else { ok = false; } } else if(name == "pri") { int32_t priValue; if(util::parseIntNoThrow(priValue, value.begin(), value.end())) { if(1 <= priValue && priValue <= 999999) { result.pri = priValue; } } } else if(name == "geo") { util::lowercase(value); result.geo = value; } } } return ok; } } // namespace // Metalink/HTTP is defined by http://tools.ietf.org/html/rfc6249. // Link header field is defined by http://tools.ietf.org/html/rfc5988. void HttpResponse::getMetalinKHttpEntries (std::vector& result, const SharedHandle