/
githubmirror
/
aria2
Обзор
Документация
Войти
/
githubmirror
/
aria2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/HttpResponse.cc
436 строк
12 KB
Tatsuhiro Tsujikawa
Enable IndentPPDirectives
09 июн 2018, 10:23
09 июн 2018, 10:23
e0a827f
Код
Авторство
О чём код?
/* <!-- copyright */ /* * aria2 - The high speed download utility * * Copyright (C) 2006 Tatsuhiro Tsujikawa * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ /* copyright --> */ #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" #include "array_fun.h" #include "MessageDigest.h" #ifdef HAVE_ZLIB # include "GZipDecodingStreamFilter.h" #endif // HAVE_ZLIB namespace aria2 { HttpResponse::HttpResponse() : cuid_{0} {} void HttpResponse::validateResponse() const { int statusCode = getStatusCode(); switch (statusCode) { case 200: // OK case 206: // Partial Content if (!httpHeader_->defined(HttpHeader::TRANSFER_ENCODING)) { // compare the received range against the requested range auto responseRange = httpHeader_->getRange(); if (!httpRequest_->isRangeSatisfied(responseRange)) { throw DL_ABORT_EX2( fmt(EX_INVALID_RANGE_HEADER, httpRequest_->getStartByte(), httpRequest_->getEndByte(), httpRequest_->getEntityLength(), responseRange.startByte, responseRange.endByte, responseRange.entityLength), error_code::CANNOT_RESUME); } } return; case 304: // Not Modified if (!httpRequest_->conditionalRequest()) { throw DL_ABORT_EX2("Got 304 without If-Modified-Since or If-None-Match", error_code::HTTP_PROTOCOL_ERROR); } return; case 300: // Multiple Choices case 301: // Moved Permanently case 302: // Found case 303: // See Other case 307: // Temporary Redirect case 308: // Permanent Redirect if (!httpHeader_->defined(HttpHeader::LOCATION)) { throw DL_ABORT_EX2(fmt(EX_LOCATION_HEADER_REQUIRED, statusCode), error_code::HTTP_PROTOCOL_ERROR); } return; } if (statusCode >= 400) { return; } throw DL_ABORT_EX2(fmt("Unexpected status %d", statusCode), error_code::HTTP_PROTOCOL_ERROR); } std::string HttpResponse::determineFilename(bool contentDispositionUTF8) const { std::string contentDisposition = util::getContentDispositionFilename( httpHeader_->find(HttpHeader::CONTENT_DISPOSITION), contentDispositionUTF8); if (contentDisposition.empty()) { auto file = httpRequest_->getFile(); file = util::percentDecode(file.begin(), file.end()); if (file.empty()) { return Request::DEFAULT_FILE; } return file; } A2_LOG_INFO( fmt(MSG_CONTENT_DISPOSITION_DETECTED, cuid_, contentDisposition.c_str())); return contentDisposition; } void HttpResponse::retrieveCookie() { Time now; auto r = httpHeader_->equalRange(HttpHeader::SET_COOKIE); for (; r.first != r.second; ++r.first) { httpRequest_->getCookieStorage()->parseAndStore( (*r.first).second, httpRequest_->getHost(), httpRequest_->getDir(), now.getTimeFromEpoch()); } } bool HttpResponse::isRedirect() const { switch (getStatusCode()) { case 300: // Multiple Choices case 301: // Moved Permanently case 302: // Found case 303: // See Other case 307: // Temporary Redirect case 308: // Permanent Redirect return httpHeader_->defined(HttpHeader::LOCATION); } return false; } void HttpResponse::processRedirect() { const auto& req = httpRequest_->getRequest(); if (!req->redirectUri(util::percentEncodeMini(getRedirectURI()))) { throw DL_RETRY_EX(fmt( "CUID#%" PRId64 " - Redirect to %s failed. It may not be a valid URI.", cuid_, req->getCurrentUri().c_str())); } A2_LOG_NOTICE(fmt(MSG_REDIRECT, cuid_, httpRequest_->getRequest()->getCurrentUri().c_str())); } const std::string& HttpResponse::getRedirectURI() const { return httpHeader_->find(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_->find(HttpHeader::TRANSFER_ENCODING); } std::unique_ptr<StreamFilter> HttpResponse::getTransferEncodingStreamFilter() const { // TODO Transfer-Encoding header field can contains multiple tokens. We should // parse the field and retrieve each token. if (isTransferEncodingSpecified()) { if (util::strieq(getTransferEncoding(), "chunked")) { return make_unique<ChunkedDecodingStreamFilter>(); } } return nullptr; } bool HttpResponse::isContentEncodingSpecified() const { return httpHeader_->defined(HttpHeader::CONTENT_ENCODING); } const std::string& HttpResponse::getContentEncoding() const { return httpHeader_->find(HttpHeader::CONTENT_ENCODING); } std::unique_ptr<StreamFilter> HttpResponse::getContentEncodingStreamFilter() const { #ifdef HAVE_ZLIB if (util::strieq(getContentEncoding(), "gzip") || util::strieq(getContentEncoding(), "deflate")) { return make_unique<GZipDecodingStreamFilter>(); } #endif // HAVE_ZLIB return nullptr; } int64_t HttpResponse::getContentLength() const { if (!httpHeader_) { return 0; } return httpHeader_->getRange().getContentLength(); } int64_t HttpResponse::getEntityLength() const { if (!httpHeader_) { return 0; } return httpHeader_->getRange().entityLength; } std::string HttpResponse::getContentType() const { if (!httpHeader_) { return A2STR::NIL; } const auto& ctype = httpHeader_->find(HttpHeader::CONTENT_TYPE); auto i = std::find(ctype.begin(), ctype.end(), ';'); Scip p = util::stripIter(ctype.begin(), i); return std::string(p.first, p.second); } void HttpResponse::setHttpHeader(std::unique_ptr<HttpHeader> httpHeader) { httpHeader_ = std::move(httpHeader); } const std::unique_ptr<HttpHeader>& HttpResponse::getHttpHeader() const { return httpHeader_; } void HttpResponse::setHttpRequest(std::unique_ptr<HttpRequest> httpRequest) { httpRequest_ = std::move(httpRequest); } int HttpResponse::getStatusCode() const { return httpHeader_->getStatusCode(); } Time HttpResponse::getLastModifiedTime() const { return Time::parseHTTPDate(httpHeader_->find(HttpHeader::LAST_MODIFIED)); } bool HttpResponse::supportsPersistentConnection() const { return httpHeader_->isKeepAlive(); } namespace { bool parseMetalinkHttpLink(MetalinkHttpEntry& result, const std::string& s) { const auto first = std::find(s.begin(), s.end(), '<'); if (first == s.end()) { return false; } auto last = std::find(first, s.end(), '>'); if (last == s.end()) { return false; } auto p = util::stripIter(first + 1, last); if (p.first == p.second) { return false; } 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; auto r = util::nextParam(name, value, last, s.end(), ';'); last = r.first; if (!r.second) { break; } if (value.empty()) { if (name == "pref") { result.pref = true; } continue; } if (name == "rel") { if (value == "duplicate") { ok = true; } else { ok = false; } continue; } if (name == "pri") { int32_t priValue; if (util::parseIntNoThrow(priValue, value)) { if (1 <= priValue && priValue <= 999999) { result.pri = priValue; } } continue; } if (name == "geo") { util::lowercase(value); result.geo = value; continue; } } 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<MetalinkHttpEntry>& result, const std::shared_ptr<Option>& option) const { auto p = httpHeader_->equalRange(HttpHeader::LINK); for (; p.first != p.second; ++p.first) { MetalinkHttpEntry e; if (parseMetalinkHttpLink(e, (*p.first).second)) { result.push_back(e); } } if (!result.empty()) { std::vector<std::string> locs; if (option->defined(PREF_METALINK_LOCATION)) { const std::string& loc = option->get(PREF_METALINK_LOCATION); util::split(loc.begin(), loc.end(), std::back_inserter(locs), ',', true); for (auto& l : locs) { util::lowercase(l); } } for (auto& r : result) { if (std::find(locs.begin(), locs.end(), r.geo) != locs.end()) { r.pri -= 999999; } } } std::sort(result.begin(), result.end()); } // Digest header field is defined by // http://tools.ietf.org/html/rfc3230. void HttpResponse::getDigest(std::vector<Checksum>& result) const { auto p = httpHeader_->equalRange(HttpHeader::DIGEST); for (; p.first != p.second; ++p.first) { const std::string& s = (*p.first).second; std::string::const_iterator itr = s.begin(); while (1) { std::string hashType, digest; auto r = util::nextParam(hashType, digest, itr, s.end(), ','); itr = r.first; if (!r.second) { break; } util::lowercase(hashType); digest = base64::decode(digest.begin(), digest.end()); if (!MessageDigest::supports(hashType) || MessageDigest::getDigestLength(hashType) != digest.size()) { continue; } result.push_back(Checksum(hashType, digest)); } } std::sort(result.begin(), result.end(), HashTypeStronger()); std::vector<Checksum> temp; for (auto i = result.begin(), eoi = result.end(); i != eoi;) { bool ok = true; auto j = i + 1; for (; j != eoi; ++j) { if ((*i).getHashType() != (*j).getHashType()) { break; } if ((*i).getDigest() != (*j).getDigest()) { ok = false; } } if (ok) { temp.push_back(*i); } i = j; } std::swap(temp, result); } } // namespace aria2