/* */ #include "HttpHeader.h" #include "Range.h" #include "util.h" #include "A2STR.h" #include "DownloadFailureException.h" #include "array_fun.h" namespace aria2 { HttpHeader::HttpHeader() : statusCode_(0) {} HttpHeader::~HttpHeader() {} void HttpHeader::put(int hdKey, const std::string& value) { std::multimap::value_type vt(hdKey, value); table_.insert(vt); } bool HttpHeader::defined(int hdKey) const { return table_.count(hdKey); } const std::string& HttpHeader::find(int hdKey) const { std::multimap::const_iterator itr = table_.find(hdKey); if(itr == table_.end()) { return A2STR::NIL; } else { return (*itr).second; } } std::vector HttpHeader::findAll(int hdKey) const { std::vector v; std::pair::const_iterator, std::multimap::const_iterator> itrpair = table_.equal_range(hdKey); while(itrpair.first != itrpair.second) { v.push_back((*itrpair.first).second); ++itrpair.first; } return v; } std::pair::const_iterator, std::multimap::const_iterator> HttpHeader::equalRange(int hdKey) const { return table_.equal_range(hdKey); } Range HttpHeader::getRange() const { const std::string& rangeStr = find(CONTENT_RANGE); if(rangeStr.empty()) { const std::string& clenStr = find(CONTENT_LENGTH); if(clenStr.empty()) { return Range(); } else { int64_t contentLength; if(!util::parseLLIntNoThrow(contentLength, clenStr) || contentLength < 0) { throw DL_ABORT_EX("Content-Length must be positive integer"); } else if(contentLength > std::numeric_limits::max()) { throw DOWNLOAD_FAILURE_EXCEPTION (fmt(EX_TOO_LARGE_FILE, contentLength)); } else if(contentLength == 0) { return Range(); } else { return Range(0, contentLength - 1, contentLength); } } } // we expect that rangeStr looks like 'bytes 100-199/100' // but some server returns '100-199/100', omitting bytes-unit sepcifier // 'bytes'. std::string::const_iterator byteRangeSpec = std::find(rangeStr.begin(), rangeStr.end(), ' '); if(byteRangeSpec == rangeStr.end()) { // we assume bytes-unit specifier omitted. byteRangeSpec = rangeStr.begin(); } else { while(byteRangeSpec != rangeStr.end() && (*byteRangeSpec == ' ' || *byteRangeSpec == '\t')) { ++byteRangeSpec; } } std::string::const_iterator slash = std::find(byteRangeSpec, rangeStr.end(), '/'); if(slash == rangeStr.end() || slash+1 == rangeStr.end() || (byteRangeSpec+1 == slash && *byteRangeSpec == '*') || (slash+2 == rangeStr.end() && *(slash+1) == '*')) { // If byte-range-resp-spec or instance-length is "*", we returns // empty Range. The former is usually sent with 416 (Request range // not satisfiable) status. return Range(); } std::string::const_iterator minus = std::find(byteRangeSpec, slash, '-'); if(minus == slash) { return Range(); } int64_t startByte, endByte, entityLength; if(!util::parseLLIntNoThrow(startByte, std::string(byteRangeSpec, minus)) || !util::parseLLIntNoThrow(endByte, std::string(minus+1, slash)) || !util::parseLLIntNoThrow(entityLength, std::string(slash+1, rangeStr.end())) || startByte < 0 || endByte < 0 || entityLength < 0) { throw DL_ABORT_EX("byte-range-spec must be positive"); } if(startByte > std::numeric_limits::max()) { throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, startByte)); } if(endByte > std::numeric_limits::max()) { throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, endByte)); } if(entityLength > std::numeric_limits::max()) { throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, entityLength)); } return Range(startByte, endByte, entityLength); } void HttpHeader::setVersion(const std::string& version) { version_ = version; } void HttpHeader::setMethod(const std::string& method) { method_ = method; } void HttpHeader::setRequestPath(const std::string& requestPath) { requestPath_ = requestPath; } void HttpHeader::clearField() { table_.clear(); } int HttpHeader::getStatusCode() const { return statusCode_; } void HttpHeader::setStatusCode(int code) { statusCode_ = code; } const std::string& HttpHeader::getVersion() const { return version_; } const std::string& HttpHeader::getMethod() const { return method_; } const std::string& HttpHeader::getRequestPath() const { return requestPath_; } const std::string& HttpHeader::getReasonPhrase() const { return reasonPhrase_; } void HttpHeader::setReasonPhrase(const std::string& reasonPhrase) { reasonPhrase_ = reasonPhrase; } bool HttpHeader::fieldContains(int hdKey, const char* value) { std::pair::const_iterator, std::multimap::const_iterator> range = equalRange(hdKey); for(std::multimap::const_iterator i = range.first; i != range.second; ++i) { std::vector values; util::splitIter((*i).second.begin(), (*i).second.end(), std::back_inserter(values), ',', true // doStrip ); for(std::vector::const_iterator j = values.begin(), eoj = values.end(); j != eoj; ++j) { if(util::strieq((*j).first, (*j).second, value)) { return true; } } } return false; } bool HttpHeader::isKeepAlive() const { const std::string& connection = find(CONNECTION); return !util::strieq(connection, "close") && (version_ == "HTTP/1.1" || util::strieq(connection, "keep-alive")); } namespace { const char* INTERESTING_HEADER_NAMES[] = { "accept-encoding", "access-control-request-headers", "access-control-request-method", "authorization", "connection", "content-disposition", "content-encoding", "content-length", "content-range", "content-type", "digest", "infohash", "last-modified", "link", "location", "origin", "port", "retry-after", "sec-websocket-key", "sec-websocket-version", "set-cookie", "transfer-encoding", "upgrade", }; } // namespace int idInterestingHeader(const char* hdName) { const char** i = std::lower_bound(vbegin(INTERESTING_HEADER_NAMES), vend(INTERESTING_HEADER_NAMES), hdName, util::strless); if(i != vend(INTERESTING_HEADER_NAMES) && strcmp(*i, hdName) == 0 ) { return i - vbegin(INTERESTING_HEADER_NAMES); } else { return HttpHeader::MAX_INTERESTING_HEADER; } } } // namespace aria2