1 /*
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 
11 #ifndef RTC_BASE_HTTPCOMMON_H_
12 #define RTC_BASE_HTTPCOMMON_H_
13 
14 #include <map>
15 #include <memory>
16 #include <string>
17 #include <vector>
18 #include "rtc_base/basictypes.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/stream.h"
21 #include "rtc_base/stringutils.h"
22 
23 namespace rtc {
24 
25 class CryptString;
26 class SocketAddress;
27 
28 //////////////////////////////////////////////////////////////////////
29 // Constants
30 //////////////////////////////////////////////////////////////////////
31 
32 enum 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 
57 enum HttpVersion {
58   HVER_1_0, HVER_1_1, HVER_UNKNOWN,
59   HVER_LAST = HVER_UNKNOWN
60 };
61 
62 enum HttpVerb {
63   HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD,
64   HV_LAST = HV_HEAD
65 };
66 
67 enum HttpError {
68   HE_NONE,
69   HE_PROTOCOL,            // Received non-valid HTTP data
70   HE_DISCONNECTED,        // Connection closed unexpectedly
71   HE_OVERFLOW,            // Received too much data for internal buffers
72   HE_CONNECT_FAILED,      // The socket failed to connect.
73   HE_SOCKET_ERROR,        // An error occurred on a connected socket
74   HE_SHUTDOWN,            // Http object is being destroyed
75   HE_OPERATION_CANCELLED, // Connection aborted locally
76   HE_AUTH,                // Proxy Authentication Required
77   HE_CERTIFICATE_EXPIRED, // During SSL negotiation
78   HE_STREAM,              // Problem reading or writing to the document
79   HE_CACHE,               // Problem reading from cache
80   HE_DEFAULT
81 };
82 
83 enum HttpHeader {
84   HH_AGE,
85   HH_CACHE_CONTROL,
86   HH_CONNECTION,
87   HH_CONTENT_DISPOSITION,
88   HH_CONTENT_LENGTH,
89   HH_CONTENT_RANGE,
90   HH_CONTENT_TYPE,
91   HH_COOKIE,
92   HH_DATE,
93   HH_ETAG,
94   HH_EXPIRES,
95   HH_HOST,
96   HH_IF_MODIFIED_SINCE,
97   HH_IF_NONE_MATCH,
98   HH_KEEP_ALIVE,
99   HH_LAST_MODIFIED,
100   HH_LOCATION,
101   HH_PROXY_AUTHENTICATE,
102   HH_PROXY_AUTHORIZATION,
103   HH_PROXY_CONNECTION,
104   HH_RANGE,
105   HH_SET_COOKIE,
106   HH_TE,
107   HH_TRAILERS,
108   HH_TRANSFER_ENCODING,
109   HH_UPGRADE,
110   HH_USER_AGENT,
111   HH_WWW_AUTHENTICATE,
112   HH_LAST = HH_WWW_AUTHENTICATE
113 };
114 
115 const uint16_t HTTP_DEFAULT_PORT = 80;
116 const uint16_t HTTP_SECURE_PORT = 443;
117 
118 //////////////////////////////////////////////////////////////////////
119 // Utility Functions
120 //////////////////////////////////////////////////////////////////////
121 
122 inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) {
123   return (err != HE_NONE) ? err : def_err;
124 }
125 
126 const char* ToString(HttpVersion version);
127 bool FromString(HttpVersion& version, const std::string& str);
128 
129 const char* ToString(HttpVerb verb);
130 bool FromString(HttpVerb& verb, const std::string& str);
131 
132 const char* ToString(HttpHeader header);
133 bool FromString(HttpHeader& header, const std::string& str);
134 
HttpCodeIsInformational(uint32_t code)135 inline bool HttpCodeIsInformational(uint32_t code) {
136   return ((code / 100) == 1);
137 }
HttpCodeIsSuccessful(uint32_t code)138 inline bool HttpCodeIsSuccessful(uint32_t code) {
139   return ((code / 100) == 2);
140 }
HttpCodeIsRedirection(uint32_t code)141 inline bool HttpCodeIsRedirection(uint32_t code) {
142   return ((code / 100) == 3);
143 }
HttpCodeIsClientError(uint32_t code)144 inline bool HttpCodeIsClientError(uint32_t code) {
145   return ((code / 100) == 4);
146 }
HttpCodeIsServerError(uint32_t code)147 inline bool HttpCodeIsServerError(uint32_t code) {
148   return ((code / 100) == 5);
149 }
150 
151 bool HttpCodeHasBody(uint32_t code);
152 bool HttpCodeIsCacheable(uint32_t code);
153 bool HttpHeaderIsEndToEnd(HttpHeader header);
154 bool HttpHeaderIsCollapsible(HttpHeader header);
155 
156 struct HttpData;
157 bool HttpShouldKeepAlive(const HttpData& data);
158 
159 typedef std::pair<std::string, std::string> HttpAttribute;
160 typedef std::vector<HttpAttribute> HttpAttributeList;
161 void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
162                            std::string* composed);
163 void HttpParseAttributes(const char * data, size_t len,
164                          HttpAttributeList& attributes);
165 bool HttpHasAttribute(const HttpAttributeList& attributes,
166                       const std::string& name,
167                       std::string* value);
168 bool HttpHasNthAttribute(HttpAttributeList& attributes,
169                          size_t index,
170                          std::string* name,
171                          std::string* value);
172 
173 // Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp
174 bool HttpDateToSeconds(const std::string& date, time_t* seconds);
175 
HttpDefaultPort(bool secure)176 inline uint16_t HttpDefaultPort(bool secure) {
177   return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT;
178 }
179 
180 // Returns the http server notation for a given address
181 std::string HttpAddress(const SocketAddress& address, bool secure);
182 
183 // functional for insensitive std::string compare
184 struct iless {
operatoriless185   bool operator()(const std::string& lhs, const std::string& rhs) const {
186     return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0);
187   }
188 };
189 
190 // put quotes around a string and escape any quotes inside it
191 std::string quote(const std::string& str);
192 
193 //////////////////////////////////////////////////////////////////////
194 // Url
195 //////////////////////////////////////////////////////////////////////
196 
197 template<class CTYPE>
198 class Url {
199 public:
200   typedef typename Traits<CTYPE>::string string;
201 
202   // TODO: Implement Encode/Decode
203   static int Encode(const CTYPE* source, CTYPE* destination, size_t len);
204   static int Encode(const string& source, string& destination);
205   static int Decode(const CTYPE* source, CTYPE* destination, size_t len);
206   static int Decode(const string& source, string& destination);
207 
Url(const string & url)208   Url(const string& url) { do_set_url(url.c_str(), url.size()); }
209   Url(const string& path, const string& host, uint16_t port = HTTP_DEFAULT_PORT)
host_(host)210       : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port) {
211     set_full_path(path);
212   }
213 
valid()214   bool valid() const { return !host_.empty(); }
clear()215   void clear() {
216     host_.clear();
217     port_ = HTTP_DEFAULT_PORT;
218     secure_ = false;
219     path_.assign(1, static_cast<CTYPE>('/'));
220     query_.clear();
221   }
222 
set_url(const string & val)223   void set_url(const string& val) {
224     do_set_url(val.c_str(), val.size());
225   }
url()226   string url() const {
227     string val; do_get_url(&val); return val;
228   }
229 
set_address(const string & val)230   void set_address(const string& val) {
231     do_set_address(val.c_str(), val.size());
232   }
address()233   string address() const {
234     string val; do_get_address(&val); return val;
235   }
236 
set_full_path(const string & val)237   void set_full_path(const string& val) {
238     do_set_full_path(val.c_str(), val.size());
239   }
full_path()240   string full_path() const {
241     string val; do_get_full_path(&val); return val;
242   }
243 
set_host(const string & val)244   void set_host(const string& val) { host_ = val; }
host()245   const string& host() const { return host_; }
246 
set_port(uint16_t val)247   void set_port(uint16_t val) { port_ = val; }
port()248   uint16_t port() const { return port_; }
249 
set_secure(bool val)250   void set_secure(bool val) { secure_ = val; }
secure()251   bool secure() const { return secure_; }
252 
set_path(const string & val)253   void set_path(const string& val) {
254     if (val.empty()) {
255       path_.assign(1, static_cast<CTYPE>('/'));
256     } else {
257       RTC_DCHECK(val[0] == static_cast<CTYPE>('/'));
258       path_ = val;
259     }
260   }
path()261   const string& path() const { return path_; }
262 
set_query(const string & val)263   void set_query(const string& val) {
264     RTC_DCHECK(val.empty() || (val[0] == static_cast<CTYPE>('?')));
265     query_ = val;
266   }
query()267   const string& query() const { return query_; }
268 
269   bool get_attribute(const string& name, string* value) const;
270 
271 private:
272   void do_set_url(const CTYPE* val, size_t len);
273   void do_set_address(const CTYPE* val, size_t len);
274   void do_set_full_path(const CTYPE* val, size_t len);
275 
276   void do_get_url(string* val) const;
277   void do_get_address(string* val) const;
278   void do_get_full_path(string* val) const;
279 
280   string host_, path_, query_;
281   uint16_t port_;
282   bool secure_;
283 };
284 
285 //////////////////////////////////////////////////////////////////////
286 // HttpData
287 //////////////////////////////////////////////////////////////////////
288 
289 struct HttpData {
290   typedef std::multimap<std::string, std::string, iless> HeaderMap;
291   typedef HeaderMap::const_iterator const_iterator;
292   typedef HeaderMap::iterator iterator;
293 
294   HttpVersion version;
295   std::unique_ptr<StreamInterface> document;
296 
297   HttpData();
298 
299   enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW };
300   void changeHeader(const std::string& name, const std::string& value,
301                     HeaderCombine combine);
302   inline void addHeader(const std::string& name, const std::string& value,
303                         bool append = true) {
304     changeHeader(name, value, append ? HC_AUTO : HC_NO);
305   }
306   inline void setHeader(const std::string& name, const std::string& value,
307                         bool overwrite = true) {
308     changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW);
309   }
310   // Returns count of erased headers
311   size_t clearHeader(const std::string& name);
312   // Returns iterator to next header
313   iterator clearHeader(iterator header);
314 
315   // keep in mind, this may not do what you want in the face of multiple headers
316   bool hasHeader(const std::string& name, std::string* value) const;
317 
beginHttpData318   inline const_iterator begin() const {
319     return headers_.begin();
320   }
endHttpData321   inline const_iterator end() const {
322     return headers_.end();
323   }
beginHttpData324   inline iterator begin() {
325     return headers_.begin();
326   }
endHttpData327   inline iterator end() {
328     return headers_.end();
329   }
beginHttpData330   inline const_iterator begin(const std::string& name) const {
331     return headers_.lower_bound(name);
332   }
endHttpData333   inline const_iterator end(const std::string& name) const {
334     return headers_.upper_bound(name);
335   }
beginHttpData336   inline iterator begin(const std::string& name) {
337     return headers_.lower_bound(name);
338   }
endHttpData339   inline iterator end(const std::string& name) {
340     return headers_.upper_bound(name);
341   }
342 
343   // Convenience methods using HttpHeader
changeHeaderHttpData344   inline void changeHeader(HttpHeader header, const std::string& value,
345                            HeaderCombine combine) {
346     changeHeader(ToString(header), value, combine);
347   }
348   inline void addHeader(HttpHeader header, const std::string& value,
349                         bool append = true) {
350     addHeader(ToString(header), value, append);
351   }
352   inline void setHeader(HttpHeader header, const std::string& value,
353                         bool overwrite = true) {
354     setHeader(ToString(header), value, overwrite);
355   }
clearHeaderHttpData356   inline void clearHeader(HttpHeader header) {
357     clearHeader(ToString(header));
358   }
hasHeaderHttpData359   inline bool hasHeader(HttpHeader header, std::string* value) const {
360     return hasHeader(ToString(header), value);
361   }
beginHttpData362   inline const_iterator begin(HttpHeader header) const {
363     return headers_.lower_bound(ToString(header));
364   }
endHttpData365   inline const_iterator end(HttpHeader header) const {
366     return headers_.upper_bound(ToString(header));
367   }
beginHttpData368   inline iterator begin(HttpHeader header) {
369     return headers_.lower_bound(ToString(header));
370   }
endHttpData371   inline iterator end(HttpHeader header) {
372     return headers_.upper_bound(ToString(header));
373   }
374 
375   void setContent(const std::string& content_type, StreamInterface* document);
376   void setDocumentAndLength(StreamInterface* document);
377 
378   virtual size_t formatLeader(char* buffer, size_t size) const = 0;
379   virtual HttpError parseLeader(const char* line, size_t len) = 0;
380 
381 protected:
382  virtual ~HttpData();
383   void clear(bool release_document);
384   void copy(const HttpData& src);
385 
386 private:
387   HeaderMap headers_;
388 };
389 
390 struct HttpRequestData : public HttpData {
391   HttpVerb verb;
392   std::string path;
393 
HttpRequestDataHttpRequestData394   HttpRequestData() : verb(HV_GET) { }
395 
396   void clear(bool release_document);
397   void copy(const HttpRequestData& src);
398 
399   size_t formatLeader(char* buffer, size_t size) const override;
400   HttpError parseLeader(const char* line, size_t len) override;
401 
402   bool getAbsoluteUri(std::string* uri) const;
403   bool getRelativeUri(std::string* host, std::string* path) const;
404 };
405 
406 struct HttpResponseData : public HttpData {
407   uint32_t scode;
408   std::string message;
409 
HttpResponseDataHttpResponseData410   HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { }
411   void clear(bool release_document);
412   void copy(const HttpResponseData& src);
413 
414   // Convenience methods
415   void set_success(uint32_t scode = HC_OK);
416   void set_success(const std::string& content_type,
417                    StreamInterface* document,
418                    uint32_t scode = HC_OK);
419   void set_redirect(const std::string& location,
420                     uint32_t scode = HC_MOVED_TEMPORARILY);
421   void set_error(uint32_t scode);
422 
423   size_t formatLeader(char* buffer, size_t size) const override;
424   HttpError parseLeader(const char* line, size_t len) override;
425 };
426 
427 struct HttpTransaction {
428   HttpRequestData request;
429   HttpResponseData response;
430 };
431 
432 //////////////////////////////////////////////////////////////////////
433 // Http Authentication
434 //////////////////////////////////////////////////////////////////////
435 
436 struct HttpAuthContext {
437   std::string auth_method;
HttpAuthContextHttpAuthContext438   HttpAuthContext(const std::string& auth) : auth_method(auth) { }
~HttpAuthContextHttpAuthContext439   virtual ~HttpAuthContext() { }
440 };
441 
442 enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR };
443 
444 // 'context' is used by this function to record information between calls.
445 // Start by passing a null pointer, then pass the same pointer each additional
446 // call.  When the authentication attempt is finished, delete the context.
447 HttpAuthResult HttpAuthenticate(
448   const char * challenge, size_t len,
449   const SocketAddress& server,
450   const std::string& method, const std::string& uri,
451   const std::string& username, const CryptString& password,
452   HttpAuthContext *& context, std::string& response, std::string& auth_method);
453 
454 //////////////////////////////////////////////////////////////////////
455 
456 } // namespace rtc
457 
458 #endif // RTC_BASE_HTTPCOMMON_H_
459