aria2/src/HttpHeader.cc

287 lines
8.2 KiB
C++

/* <!-- 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);
}
bool HttpHeader::defined(int hdKey) const
{
return table_.count(hdKey);
}
const std::string& HttpHeader::find(int hdKey) const
{
std::multimap<int, std::string>::const_iterator 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;
std::pair<std::multimap<int, std::string>::const_iterator,
std::multimap<int, std::string>::const_iterator> 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 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<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/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<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(std::multimap<int, std::string>::const_iterator 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(std::vector<Scip>::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