/* */ #include "HttpResponse.h" #include #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 "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" namespace aria2 { HttpResponse::HttpResponse():cuid(0), logger(LogFactory::getInstance()) {} HttpResponse::~HttpResponse() {} void HttpResponse::validateResponse() const { const std::string& status = getResponseStatus(); if(status >= HttpHeader::S400) { return; } if(status >= HttpHeader::S300) { if(!httpHeader->defined(HttpHeader::LOCATION)) { throw DlAbortEx (StringFormat(EX_LOCATION_HEADER_REQUIRED, Util::parseUInt(status)).str()); } } else if(!httpHeader->defined(HttpHeader::TRANSFER_ENCODING)) { // compare the received range against the requested range RangeHandle responseRange = httpHeader->getRange(); if(!httpRequest->isRangeSatisfied(responseRange)) { throw DlAbortEx (StringFormat (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()).str()); } } } std::string HttpResponse::determinFilename() const { std::string contentDisposition = Util::getContentDispositionFilename (httpHeader->getFirst(HttpHeader::CONTENT_DISPOSITION)); if(contentDisposition.empty()) { return Util::urldecode(httpRequest->getFile()); } else { logger->info(MSG_CONTENT_DISPOSITION_DETECTED, cuid, contentDisposition.c_str()); return Util::urldecode(contentDisposition); } } void HttpResponse::retrieveCookie() { std::deque v = httpHeader->get(HttpHeader::SET_COOKIE); for(std::deque::const_iterator itr = v.begin(); itr != v.end(); ++itr) { httpRequest->getCookieStorage()->parseAndStore(*itr, httpRequest->getHost(), httpRequest->getDir()); } } bool HttpResponse::isRedirect() const { const std::string& status = getResponseStatus(); return HttpHeader::S300 <= status && status < HttpHeader::S400 && httpHeader->defined(HttpHeader::LOCATION); } void HttpResponse::processRedirect() { if(httpRequest->getRequest()->redirectUrl(getRedirectURI())) { logger->info(MSG_REDIRECT, cuid, httpRequest->getRequest()->getCurrentUrl().c_str()); } else { throw DlRetryEx (StringFormat("CUID#%d - Redirect to %s failed. It may not be a valid" " URI.", cuid, httpRequest->getRequest()->getCurrentUrl().c_str()).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 getTransferEncodingDecoder() return httpHeader->getFirst(HttpHeader::TRANSFER_ENCODING); } SharedHandle HttpResponse::getTransferEncodingDecoder() const { // 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(new ChunkedDecoder()); } } return SharedHandle(); } bool HttpResponse::isContentEncodingSpecified() const { return httpHeader->defined(HttpHeader::CONTENT_ENCODING); } const std::string& HttpResponse::getContentEncoding() const { return httpHeader->getFirst(HttpHeader::CONTENT_ENCODING); } SharedHandle HttpResponse::getContentEncodingDecoder() const { #ifdef HAVE_LIBZ if(getContentEncoding() == HttpHeader::GZIP || getContentEncoding() == HttpHeader::DEFLATE) { return SharedHandle(new GZipDecoder()); } #endif // HAVE_LIBZ return SharedHandle(); } uint64_t HttpResponse::getContentLength() const { if(httpHeader.isNull()) { return 0; } else { return httpHeader->getRange()->getContentLength(); } } uint64_t HttpResponse::getEntityLength() const { if(httpHeader.isNull()) { return 0; } else { return httpHeader->getRange()->getEntityLength(); } } std::string HttpResponse::getContentType() const { if(httpHeader.isNull()) { return A2STR::NIL; } else { return Util::split(httpHeader->getFirst(HttpHeader::CONTENT_TYPE), ";").first; } } void HttpResponse::setHttpHeader(const SharedHandle& httpHeader) { this->httpHeader = httpHeader; } SharedHandle HttpResponse::getHttpHeader() const { return httpHeader; } void HttpResponse::setHttpRequest(const SharedHandle& httpRequest) { this->httpRequest = httpRequest; } SharedHandle HttpResponse::getHttpRequest() const { return httpRequest; } // TODO return std::string const std::string& HttpResponse::getResponseStatus() const { return httpHeader->getResponseStatus(); } 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