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