1 /* <!-- copyright */
2 /*
3 * aria2 - The high speed download utility
4 *
5 * Copyright (C) 2006 Tatsuhiro Tsujikawa
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 * In addition, as a special exception, the copyright holders give
22 * permission to link the code of portions of this program with the
23 * OpenSSL library under certain conditions as described in each
24 * individual source file, and distribute linked combinations
25 * including the two.
26 * You must obey the GNU General Public License in all respects
27 * for all of the code used other than OpenSSL. If you modify
28 * file(s) with this exception, you may extend this exception to your
29 * version of the file(s), but you are not obligated to do so. If you
30 * do not wish to do so, delete this exception statement from your
31 * version. If you delete this exception statement from all source
32 * files in the program, then also delete it here.
33 */
34 /* copyright --> */
35 #include "HttpHeader.h"
36 #include "Range.h"
37 #include "util.h"
38 #include "A2STR.h"
39 #include "DownloadFailureException.h"
40 #include "array_fun.h"
41
42 namespace aria2 {
43
HttpHeader()44 HttpHeader::HttpHeader() : statusCode_(0) {}
45
46 HttpHeader::~HttpHeader() = default;
47
put(int hdKey,const std::string & value)48 void HttpHeader::put(int hdKey, const std::string& value)
49 {
50 std::multimap<int, std::string>::value_type vt(hdKey, value);
51 table_.insert(vt);
52 }
53
remove(int hdKey)54 void HttpHeader::remove(int hdKey) { table_.erase(hdKey); }
55
defined(int hdKey) const56 bool HttpHeader::defined(int hdKey) const { return table_.count(hdKey); }
57
find(int hdKey) const58 const std::string& HttpHeader::find(int hdKey) const
59 {
60 auto itr = table_.find(hdKey);
61 if (itr == table_.end()) {
62 return A2STR::NIL;
63 }
64 else {
65 return (*itr).second;
66 }
67 }
68
findAll(int hdKey) const69 std::vector<std::string> HttpHeader::findAll(int hdKey) const
70 {
71 std::vector<std::string> v;
72 auto itrpair = table_.equal_range(hdKey);
73 while (itrpair.first != itrpair.second) {
74 v.push_back((*itrpair.first).second);
75 ++itrpair.first;
76 }
77 return v;
78 }
79
80 std::pair<std::multimap<int, std::string>::const_iterator,
81 std::multimap<int, std::string>::const_iterator>
equalRange(int hdKey) const82 HttpHeader::equalRange(int hdKey) const
83 {
84 return table_.equal_range(hdKey);
85 }
86
getRange() const87 Range HttpHeader::getRange() const
88 {
89 const auto& rangeStr = find(CONTENT_RANGE);
90 if (rangeStr.empty()) {
91 const std::string& clenStr = find(CONTENT_LENGTH);
92 if (clenStr.empty()) {
93 return Range();
94 }
95 else {
96 int64_t contentLength;
97 if (!util::parseLLIntNoThrow(contentLength, clenStr) ||
98 contentLength < 0) {
99 throw DL_ABORT_EX("Content-Length must be positive integer");
100 }
101 else if (contentLength > std::numeric_limits<a2_off_t>::max()) {
102 throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, contentLength));
103 }
104 else if (contentLength == 0) {
105 return Range();
106 }
107 else {
108 return Range(0, contentLength - 1, contentLength);
109 }
110 }
111 }
112 // we expect that rangeStr looks like 'bytes 100-199/200' but some
113 // server returns '100-199/200', omitting bytes-unit specifier
114 // 'bytes'. Moreover, some server may return like
115 // 'bytes=100-199/200'.
116 auto byteRangeSpec = std::find(rangeStr.begin(), rangeStr.end(), ' ');
117 if (byteRangeSpec == rangeStr.end()) {
118 // check for 'bytes=100-199/200' case
119 byteRangeSpec = std::find(rangeStr.begin(), rangeStr.end(), '=');
120 if (byteRangeSpec == rangeStr.end()) {
121 // we assume bytes-unit specifier omitted.
122 byteRangeSpec = rangeStr.begin();
123 }
124 else {
125 ++byteRangeSpec;
126 }
127 }
128 else {
129 while (byteRangeSpec != rangeStr.end() &&
130 (*byteRangeSpec == ' ' || *byteRangeSpec == '\t')) {
131 ++byteRangeSpec;
132 }
133 }
134 auto slash = std::find(byteRangeSpec, rangeStr.end(), '/');
135 if (slash == rangeStr.end() || slash + 1 == rangeStr.end() ||
136 (byteRangeSpec + 1 == slash && *byteRangeSpec == '*') ||
137 (slash + 2 == rangeStr.end() && *(slash + 1) == '*')) {
138 // If byte-range-resp-spec or instance-length is "*", we returns
139 // empty Range. The former is usually sent with 416 (Request range
140 // not satisfiable) status.
141 return Range();
142 }
143 auto minus = std::find(byteRangeSpec, slash, '-');
144 if (minus == slash) {
145 return Range();
146 }
147 int64_t startByte, endByte, entityLength;
148 if (!util::parseLLIntNoThrow(startByte, std::string(byteRangeSpec, minus)) ||
149 !util::parseLLIntNoThrow(endByte, std::string(minus + 1, slash)) ||
150 !util::parseLLIntNoThrow(entityLength,
151 std::string(slash + 1, rangeStr.end())) ||
152 startByte < 0 || endByte < 0 || entityLength < 0) {
153 throw DL_ABORT_EX("byte-range-spec must be positive");
154 }
155 if (startByte > std::numeric_limits<a2_off_t>::max()) {
156 throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, startByte));
157 }
158 if (endByte > std::numeric_limits<a2_off_t>::max()) {
159 throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, endByte));
160 }
161 if (entityLength > std::numeric_limits<a2_off_t>::max()) {
162 throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, entityLength));
163 }
164 return Range(startByte, endByte, entityLength);
165 }
166
setVersion(const std::string & version)167 void HttpHeader::setVersion(const std::string& version) { version_ = version; }
168
setMethod(const std::string & method)169 void HttpHeader::setMethod(const std::string& method) { method_ = method; }
170
setRequestPath(const std::string & requestPath)171 void HttpHeader::setRequestPath(const std::string& requestPath)
172 {
173 requestPath_ = requestPath;
174 }
175
clearField()176 void HttpHeader::clearField() { table_.clear(); }
177
getStatusCode() const178 int HttpHeader::getStatusCode() const { return statusCode_; }
179
setStatusCode(int code)180 void HttpHeader::setStatusCode(int code) { statusCode_ = code; }
181
getVersion() const182 const std::string& HttpHeader::getVersion() const { return version_; }
183
getMethod() const184 const std::string& HttpHeader::getMethod() const { return method_; }
185
getRequestPath() const186 const std::string& HttpHeader::getRequestPath() const { return requestPath_; }
187
getReasonPhrase() const188 const std::string& HttpHeader::getReasonPhrase() const { return reasonPhrase_; }
189
setReasonPhrase(const std::string & reasonPhrase)190 void HttpHeader::setReasonPhrase(const std::string& reasonPhrase)
191 {
192 reasonPhrase_ = reasonPhrase;
193 }
194
fieldContains(int hdKey,const char * value)195 bool HttpHeader::fieldContains(int hdKey, const char* value)
196 {
197 std::pair<std::multimap<int, std::string>::const_iterator,
198 std::multimap<int, std::string>::const_iterator>
199 range = equalRange(hdKey);
200 for (auto i = range.first; i != range.second; ++i) {
201 std::vector<Scip> values;
202 util::splitIter((*i).second.begin(), (*i).second.end(),
203 std::back_inserter(values), ',',
204 true // doStrip
205 );
206 for (const auto& v : values) {
207 if (util::strieq(v.first, v.second, value)) {
208 return true;
209 }
210 }
211 }
212 return false;
213 }
214
isKeepAlive() const215 bool HttpHeader::isKeepAlive() const
216 {
217 const std::string& connection = find(CONNECTION);
218 return !util::strieq(connection, "close") &&
219 (version_ == "HTTP/1.1" || util::strieq(connection, "keep-alive"));
220 }
221
222 namespace {
223 constexpr const char* INTERESTING_HEADER_NAMES[] = {
224 "accept-encoding",
225 "access-control-request-headers",
226 "access-control-request-method",
227 "authorization",
228 "connection",
229 "content-disposition",
230 "content-encoding",
231 "content-length",
232 "content-range",
233 "content-type",
234 "digest",
235 "infohash",
236 "last-modified",
237 "link",
238 "location",
239 "origin",
240 "port",
241 "retry-after",
242 "sec-websocket-key",
243 "sec-websocket-version",
244 "set-cookie",
245 "transfer-encoding",
246 "upgrade",
247 };
248 } // namespace
249
idInterestingHeader(const char * hdName)250 int idInterestingHeader(const char* hdName)
251 {
252 auto i = std::lower_bound(std::begin(INTERESTING_HEADER_NAMES),
253 std::end(INTERESTING_HEADER_NAMES), hdName,
254 util::strless);
255 if (i != std::end(INTERESTING_HEADER_NAMES) && strcmp(*i, hdName) == 0) {
256 return i - std::begin(INTERESTING_HEADER_NAMES);
257 }
258 else {
259 return HttpHeader::MAX_INTERESTING_HEADER;
260 }
261 }
262
263 } // namespace aria2
264