blob: 1b8768dfd20b1c7ec978fae416543ebd74b4bf55 [file] [log] [blame]
deadbeeff137e972017-03-23 15:45:49 -07001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#ifndef RTC_BASE_HTTPCOMMON_H_
12#define RTC_BASE_HTTPCOMMON_H_
deadbeeff137e972017-03-23 15:45:49 -070013
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020014#include <map>
15#include <memory>
16#include <string>
17#include <vector>
Niels Möller65ec0fc2018-05-21 11:46:20 +020018
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "rtc_base/checks.h"
20#include "rtc_base/stream.h"
21#include "rtc_base/stringutils.h"
deadbeeff137e972017-03-23 15:45:49 -070022
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020023namespace rtc {
deadbeeff137e972017-03-23 15:45:49 -070024
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020025class CryptString;
26class SocketAddress;
27
28//////////////////////////////////////////////////////////////////////
29// Constants
30//////////////////////////////////////////////////////////////////////
31
32enum HttpCode {
33 HC_OK = 200,
34 HC_NON_AUTHORITATIVE = 203,
35 HC_NO_CONTENT = 204,
36 HC_PARTIAL_CONTENT = 206,
37
38 HC_MULTIPLE_CHOICES = 300,
39 HC_MOVED_PERMANENTLY = 301,
40 HC_FOUND = 302,
41 HC_SEE_OTHER = 303,
42 HC_NOT_MODIFIED = 304,
43 HC_MOVED_TEMPORARILY = 307,
44
45 HC_BAD_REQUEST = 400,
46 HC_UNAUTHORIZED = 401,
47 HC_FORBIDDEN = 403,
48 HC_NOT_FOUND = 404,
49 HC_PROXY_AUTHENTICATION_REQUIRED = 407,
50 HC_GONE = 410,
51
52 HC_INTERNAL_SERVER_ERROR = 500,
53 HC_NOT_IMPLEMENTED = 501,
54 HC_SERVICE_UNAVAILABLE = 503,
55};
56
Yves Gerey665174f2018-06-19 15:03:05 +020057enum HttpVersion { HVER_1_0, HVER_1_1, HVER_UNKNOWN, HVER_LAST = HVER_UNKNOWN };
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020058
59enum HttpVerb {
Yves Gerey665174f2018-06-19 15:03:05 +020060 HV_GET,
61 HV_POST,
62 HV_PUT,
63 HV_DELETE,
64 HV_CONNECT,
65 HV_HEAD,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020066 HV_LAST = HV_HEAD
67};
68
69enum HttpError {
70 HE_NONE,
Yves Gerey665174f2018-06-19 15:03:05 +020071 HE_PROTOCOL, // Received non-valid HTTP data
72 HE_DISCONNECTED, // Connection closed unexpectedly
73 HE_OVERFLOW, // Received too much data for internal buffers
74 HE_CONNECT_FAILED, // The socket failed to connect.
75 HE_SOCKET_ERROR, // An error occurred on a connected socket
76 HE_SHUTDOWN, // Http object is being destroyed
77 HE_OPERATION_CANCELLED, // Connection aborted locally
78 HE_AUTH, // Proxy Authentication Required
79 HE_CERTIFICATE_EXPIRED, // During SSL negotiation
80 HE_STREAM, // Problem reading or writing to the document
81 HE_CACHE, // Problem reading from cache
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020082 HE_DEFAULT
83};
84
85enum HttpHeader {
86 HH_AGE,
87 HH_CACHE_CONTROL,
88 HH_CONNECTION,
89 HH_CONTENT_DISPOSITION,
90 HH_CONTENT_LENGTH,
91 HH_CONTENT_RANGE,
92 HH_CONTENT_TYPE,
93 HH_COOKIE,
94 HH_DATE,
95 HH_ETAG,
96 HH_EXPIRES,
97 HH_HOST,
98 HH_IF_MODIFIED_SINCE,
99 HH_IF_NONE_MATCH,
100 HH_KEEP_ALIVE,
101 HH_LAST_MODIFIED,
102 HH_LOCATION,
103 HH_PROXY_AUTHENTICATE,
104 HH_PROXY_AUTHORIZATION,
105 HH_PROXY_CONNECTION,
106 HH_RANGE,
107 HH_SET_COOKIE,
108 HH_TE,
109 HH_TRAILERS,
110 HH_TRANSFER_ENCODING,
111 HH_UPGRADE,
112 HH_USER_AGENT,
113 HH_WWW_AUTHENTICATE,
114 HH_LAST = HH_WWW_AUTHENTICATE
115};
116
117const uint16_t HTTP_DEFAULT_PORT = 80;
118const uint16_t HTTP_SECURE_PORT = 443;
119
120//////////////////////////////////////////////////////////////////////
121// Utility Functions
122//////////////////////////////////////////////////////////////////////
123
124inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) {
125 return (err != HE_NONE) ? err : def_err;
126}
127
128const char* ToString(HttpVersion version);
129bool FromString(HttpVersion& version, const std::string& str);
130
131const char* ToString(HttpVerb verb);
132bool FromString(HttpVerb& verb, const std::string& str);
133
134const char* ToString(HttpHeader header);
135bool FromString(HttpHeader& header, const std::string& str);
136
137inline bool HttpCodeIsInformational(uint32_t code) {
138 return ((code / 100) == 1);
139}
140inline bool HttpCodeIsSuccessful(uint32_t code) {
141 return ((code / 100) == 2);
142}
143inline bool HttpCodeIsRedirection(uint32_t code) {
144 return ((code / 100) == 3);
145}
146inline bool HttpCodeIsClientError(uint32_t code) {
147 return ((code / 100) == 4);
148}
149inline bool HttpCodeIsServerError(uint32_t code) {
150 return ((code / 100) == 5);
151}
152
153bool HttpCodeHasBody(uint32_t code);
154bool HttpCodeIsCacheable(uint32_t code);
155bool HttpHeaderIsEndToEnd(HttpHeader header);
156bool HttpHeaderIsCollapsible(HttpHeader header);
157
158struct HttpData;
159bool HttpShouldKeepAlive(const HttpData& data);
160
161typedef std::pair<std::string, std::string> HttpAttribute;
162typedef std::vector<HttpAttribute> HttpAttributeList;
Yves Gerey665174f2018-06-19 15:03:05 +0200163void HttpParseAttributes(const char* data,
164 size_t len,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200165 HttpAttributeList& attributes);
166bool HttpHasAttribute(const HttpAttributeList& attributes,
167 const std::string& name,
168 std::string* value);
169bool HttpHasNthAttribute(HttpAttributeList& attributes,
170 size_t index,
171 std::string* name,
172 std::string* value);
173
174// Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp
175bool HttpDateToSeconds(const std::string& date, time_t* seconds);
176
177inline uint16_t HttpDefaultPort(bool secure) {
178 return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT;
179}
180
181// Returns the http server notation for a given address
182std::string HttpAddress(const SocketAddress& address, bool secure);
183
184// functional for insensitive std::string compare
185struct iless {
186 bool operator()(const std::string& lhs, const std::string& rhs) const {
187 return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0);
188 }
189};
190
191// put quotes around a string and escape any quotes inside it
192std::string quote(const std::string& str);
193
194//////////////////////////////////////////////////////////////////////
195// Url
196//////////////////////////////////////////////////////////////////////
197
Yves Gerey665174f2018-06-19 15:03:05 +0200198template <class CTYPE>
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200199class Url {
Yves Gerey665174f2018-06-19 15:03:05 +0200200 public:
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200201 typedef typename Traits<CTYPE>::string string;
202
203 // TODO: Implement Encode/Decode
204 static int Encode(const CTYPE* source, CTYPE* destination, size_t len);
205 static int Encode(const string& source, string& destination);
206 static int Decode(const CTYPE* source, CTYPE* destination, size_t len);
207 static int Decode(const string& source, string& destination);
208
209 Url(const string& url) { do_set_url(url.c_str(), url.size()); }
210 Url(const string& path, const string& host, uint16_t port = HTTP_DEFAULT_PORT)
211 : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port) {
212 set_full_path(path);
213 }
214
215 bool valid() const { return !host_.empty(); }
216 void clear() {
217 host_.clear();
218 port_ = HTTP_DEFAULT_PORT;
219 secure_ = false;
220 path_.assign(1, static_cast<CTYPE>('/'));
221 query_.clear();
222 }
223
Yves Gerey665174f2018-06-19 15:03:05 +0200224 void set_url(const string& val) { do_set_url(val.c_str(), val.size()); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200225 string url() const {
Yves Gerey665174f2018-06-19 15:03:05 +0200226 string val;
227 do_get_url(&val);
228 return val;
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200229 }
230
231 void set_address(const string& val) {
232 do_set_address(val.c_str(), val.size());
233 }
234 string address() const {
Yves Gerey665174f2018-06-19 15:03:05 +0200235 string val;
236 do_get_address(&val);
237 return val;
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200238 }
239
240 void set_full_path(const string& val) {
241 do_set_full_path(val.c_str(), val.size());
242 }
243 string full_path() const {
Yves Gerey665174f2018-06-19 15:03:05 +0200244 string val;
245 do_get_full_path(&val);
246 return val;
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200247 }
248
249 void set_host(const string& val) { host_ = val; }
250 const string& host() const { return host_; }
251
252 void set_port(uint16_t val) { port_ = val; }
253 uint16_t port() const { return port_; }
254
255 void set_secure(bool val) { secure_ = val; }
256 bool secure() const { return secure_; }
257
258 void set_path(const string& val) {
259 if (val.empty()) {
260 path_.assign(1, static_cast<CTYPE>('/'));
261 } else {
262 RTC_DCHECK(val[0] == static_cast<CTYPE>('/'));
263 path_ = val;
264 }
265 }
266 const string& path() const { return path_; }
267
268 void set_query(const string& val) {
269 RTC_DCHECK(val.empty() || (val[0] == static_cast<CTYPE>('?')));
270 query_ = val;
271 }
272 const string& query() const { return query_; }
273
274 bool get_attribute(const string& name, string* value) const;
275
Yves Gerey665174f2018-06-19 15:03:05 +0200276 private:
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200277 void do_set_url(const CTYPE* val, size_t len);
278 void do_set_address(const CTYPE* val, size_t len);
279 void do_set_full_path(const CTYPE* val, size_t len);
280
281 void do_get_url(string* val) const;
282 void do_get_address(string* val) const;
283 void do_get_full_path(string* val) const;
284
285 string host_, path_, query_;
286 uint16_t port_;
287 bool secure_;
288};
289
290//////////////////////////////////////////////////////////////////////
291// HttpData
292//////////////////////////////////////////////////////////////////////
293
294struct HttpData {
295 typedef std::multimap<std::string, std::string, iless> HeaderMap;
296 typedef HeaderMap::const_iterator const_iterator;
297 typedef HeaderMap::iterator iterator;
298
299 HttpVersion version;
300 std::unique_ptr<StreamInterface> document;
301
302 HttpData();
303
304 enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW };
Yves Gerey665174f2018-06-19 15:03:05 +0200305 void changeHeader(const std::string& name,
306 const std::string& value,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200307 HeaderCombine combine);
Yves Gerey665174f2018-06-19 15:03:05 +0200308 inline void addHeader(const std::string& name,
309 const std::string& value,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200310 bool append = true) {
311 changeHeader(name, value, append ? HC_AUTO : HC_NO);
312 }
Yves Gerey665174f2018-06-19 15:03:05 +0200313 inline void setHeader(const std::string& name,
314 const std::string& value,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200315 bool overwrite = true) {
316 changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW);
317 }
318 // Returns count of erased headers
319 size_t clearHeader(const std::string& name);
320 // Returns iterator to next header
321 iterator clearHeader(iterator header);
322
323 // keep in mind, this may not do what you want in the face of multiple headers
324 bool hasHeader(const std::string& name, std::string* value) const;
325
Yves Gerey665174f2018-06-19 15:03:05 +0200326 inline const_iterator begin() const { return headers_.begin(); }
327 inline const_iterator end() const { return headers_.end(); }
328 inline iterator begin() { return headers_.begin(); }
329 inline iterator end() { return headers_.end(); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200330 inline const_iterator begin(const std::string& name) const {
331 return headers_.lower_bound(name);
332 }
333 inline const_iterator end(const std::string& name) const {
334 return headers_.upper_bound(name);
335 }
336 inline iterator begin(const std::string& name) {
337 return headers_.lower_bound(name);
338 }
339 inline iterator end(const std::string& name) {
340 return headers_.upper_bound(name);
341 }
342
343 // Convenience methods using HttpHeader
Yves Gerey665174f2018-06-19 15:03:05 +0200344 inline void changeHeader(HttpHeader header,
345 const std::string& value,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200346 HeaderCombine combine) {
347 changeHeader(ToString(header), value, combine);
348 }
Yves Gerey665174f2018-06-19 15:03:05 +0200349 inline void addHeader(HttpHeader header,
350 const std::string& value,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200351 bool append = true) {
352 addHeader(ToString(header), value, append);
353 }
Yves Gerey665174f2018-06-19 15:03:05 +0200354 inline void setHeader(HttpHeader header,
355 const std::string& value,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200356 bool overwrite = true) {
357 setHeader(ToString(header), value, overwrite);
358 }
Yves Gerey665174f2018-06-19 15:03:05 +0200359 inline void clearHeader(HttpHeader header) { clearHeader(ToString(header)); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200360 inline bool hasHeader(HttpHeader header, std::string* value) const {
361 return hasHeader(ToString(header), value);
362 }
363 inline const_iterator begin(HttpHeader header) const {
364 return headers_.lower_bound(ToString(header));
365 }
366 inline const_iterator end(HttpHeader header) const {
367 return headers_.upper_bound(ToString(header));
368 }
369 inline iterator begin(HttpHeader header) {
370 return headers_.lower_bound(ToString(header));
371 }
372 inline iterator end(HttpHeader header) {
373 return headers_.upper_bound(ToString(header));
374 }
375
376 void setContent(const std::string& content_type, StreamInterface* document);
377 void setDocumentAndLength(StreamInterface* document);
378
379 virtual size_t formatLeader(char* buffer, size_t size) const = 0;
380 virtual HttpError parseLeader(const char* line, size_t len) = 0;
381
Yves Gerey665174f2018-06-19 15:03:05 +0200382 protected:
383 virtual ~HttpData();
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200384 void clear(bool release_document);
385 void copy(const HttpData& src);
386
Yves Gerey665174f2018-06-19 15:03:05 +0200387 private:
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200388 HeaderMap headers_;
389};
390
391struct HttpRequestData : public HttpData {
392 HttpVerb verb;
393 std::string path;
394
Yves Gerey665174f2018-06-19 15:03:05 +0200395 HttpRequestData() : verb(HV_GET) {}
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200396
397 void clear(bool release_document);
398 void copy(const HttpRequestData& src);
399
400 size_t formatLeader(char* buffer, size_t size) const override;
401 HttpError parseLeader(const char* line, size_t len) override;
402
403 bool getAbsoluteUri(std::string* uri) const;
404 bool getRelativeUri(std::string* host, std::string* path) const;
405};
406
407struct HttpResponseData : public HttpData {
408 uint32_t scode;
409 std::string message;
410
Yves Gerey665174f2018-06-19 15:03:05 +0200411 HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) {}
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200412 void clear(bool release_document);
413 void copy(const HttpResponseData& src);
414
415 // Convenience methods
416 void set_success(uint32_t scode = HC_OK);
417 void set_success(const std::string& content_type,
418 StreamInterface* document,
419 uint32_t scode = HC_OK);
420 void set_redirect(const std::string& location,
421 uint32_t scode = HC_MOVED_TEMPORARILY);
422 void set_error(uint32_t scode);
423
424 size_t formatLeader(char* buffer, size_t size) const override;
425 HttpError parseLeader(const char* line, size_t len) override;
426};
427
428struct HttpTransaction {
429 HttpRequestData request;
430 HttpResponseData response;
431};
432
433//////////////////////////////////////////////////////////////////////
434// Http Authentication
435//////////////////////////////////////////////////////////////////////
436
437struct HttpAuthContext {
438 std::string auth_method;
Yves Gerey665174f2018-06-19 15:03:05 +0200439 HttpAuthContext(const std::string& auth) : auth_method(auth) {}
440 virtual ~HttpAuthContext() {}
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200441};
442
443enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR };
444
445// 'context' is used by this function to record information between calls.
446// Start by passing a null pointer, then pass the same pointer each additional
447// call. When the authentication attempt is finished, delete the context.
Joachim Bauch5b32f232018-03-07 20:02:26 +0100448// TODO(bugs.webrtc.org/8905): Change "response" to "ZeroOnFreeBuffer".
Yves Gerey665174f2018-06-19 15:03:05 +0200449HttpAuthResult HttpAuthenticate(const char* challenge,
450 size_t len,
451 const SocketAddress& server,
452 const std::string& method,
453 const std::string& uri,
454 const std::string& username,
455 const CryptString& password,
456 HttpAuthContext*& context,
457 std::string& response,
458 std::string& auth_method);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200459
460//////////////////////////////////////////////////////////////////////
461
Yves Gerey665174f2018-06-19 15:03:05 +0200462} // namespace rtc
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200463
Yves Gerey665174f2018-06-19 15:03:05 +0200464#endif // RTC_BASE_HTTPCOMMON_H_