| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 | /* <!-- 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 "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<int, std::string>::value_type vt(hdKey, value);  table_.insert(vt);}void HttpHeader::remove(int hdKey) { table_.erase(hdKey); }bool HttpHeader::defined(int hdKey) const { return table_.count(hdKey); }const std::string& HttpHeader::find(int hdKey) const{  auto itr = table_.find(hdKey);  if (itr == table_.end()) {    return A2STR::NIL;  }  else {    return (*itr).second;  }}std::vector<std::string> HttpHeader::findAll(int hdKey) const{  std::vector<std::string> v;  auto itrpair = table_.equal_range(hdKey);  while (itrpair.first != itrpair.second) {    v.push_back((*itrpair.first).second);    ++itrpair.first;  }  return v;}std::pair<std::multimap<int, std::string>::const_iterator,          std::multimap<int, std::string>::const_iterator>HttpHeader::equalRange(int hdKey) const{  return table_.equal_range(hdKey);}Range HttpHeader::getRange() const{  const auto& 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<a2_off_t>::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/200' but some  // server returns '100-199/200', omitting bytes-unit specifier  // 'bytes'.  Moreover, some server may return like  // 'bytes=100-199/200'.  auto byteRangeSpec = std::find(rangeStr.begin(), rangeStr.end(), ' ');  if (byteRangeSpec == rangeStr.end()) {    // check for 'bytes=100-199/200' case    byteRangeSpec = std::find(rangeStr.begin(), rangeStr.end(), '=');    if (byteRangeSpec == rangeStr.end()) {      // we assume bytes-unit specifier omitted.      byteRangeSpec = rangeStr.begin();    }    else {      ++byteRangeSpec;    }  }  else {    while (byteRangeSpec != rangeStr.end() &&           (*byteRangeSpec == ' ' || *byteRangeSpec == '\t')) {      ++byteRangeSpec;    }  }  auto 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();  }  auto 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<a2_off_t>::max()) {    throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, startByte));  }  if (endByte > std::numeric_limits<a2_off_t>::max()) {    throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, endByte));  }  if (entityLength > std::numeric_limits<a2_off_t>::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<std::multimap<int, std::string>::const_iterator,            std::multimap<int, std::string>::const_iterator> range =      equalRange(hdKey);  for (auto i = range.first; i != range.second; ++i) {    std::vector<Scip> values;    util::splitIter((*i).second.begin(), (*i).second.end(),                    std::back_inserter(values), ',',                    true // doStrip                    );    for (const auto& v : values) {      if (util::strieq(v.first, v.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",};} // namespaceint idInterestingHeader(const char* hdName){  const char** i = std::lower_bound(std::begin(INTERESTING_HEADER_NAMES),                                    std::end(INTERESTING_HEADER_NAMES), hdName,                                    util::strless);  if (i != std::end(INTERESTING_HEADER_NAMES) && strcmp(*i, hdName) == 0) {    return i - std::begin(INTERESTING_HEADER_NAMES);  }  else {    return HttpHeader::MAX_INTERESTING_HEADER;  }}} // namespace aria2
 |