1 /*
2  * libjingle
3  * Copyright 2004--2005, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifndef TALK_BASE_HTTPCOMMON_H__
29 #define TALK_BASE_HTTPCOMMON_H__
30 
31 #include <map>
32 #include <string>
33 #include <vector>
34 #include "talk/base/basictypes.h"
35 #include "talk/base/common.h"
36 #include "talk/base/scoped_ptr.h"
37 #include "talk/base/stringutils.h"
38 #include "talk/base/stream.h"
39 
40 namespace talk_base {
41 
42 class CryptString;
43 class SocketAddress;
44 
45 //////////////////////////////////////////////////////////////////////
46 // Constants
47 //////////////////////////////////////////////////////////////////////
48 
49 enum HttpCode {
50   HC_OK = 200,
51   HC_NON_AUTHORITATIVE = 203,
52   HC_NO_CONTENT = 204,
53   HC_PARTIAL_CONTENT = 206,
54 
55   HC_MULTIPLE_CHOICES = 300,
56   HC_MOVED_PERMANENTLY = 301,
57   HC_FOUND = 302,
58   HC_SEE_OTHER = 303,
59   HC_NOT_MODIFIED = 304,
60   HC_MOVED_TEMPORARILY = 307,
61 
62   HC_BAD_REQUEST = 400,
63   HC_UNAUTHORIZED = 401,
64   HC_FORBIDDEN = 403,
65   HC_NOT_FOUND = 404,
66   HC_PROXY_AUTHENTICATION_REQUIRED = 407,
67   HC_GONE = 410,
68 
69   HC_INTERNAL_SERVER_ERROR = 500,
70   HC_NOT_IMPLEMENTED = 501,
71   HC_SERVICE_UNAVAILABLE = 503,
72 };
73 
74 enum HttpVersion {
75   HVER_1_0, HVER_1_1, HVER_UNKNOWN,
76   HVER_LAST = HVER_UNKNOWN
77 };
78 
79 enum HttpVerb {
80   HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD,
81   HV_LAST = HV_HEAD
82 };
83 
84 enum HttpError {
85   HE_NONE,
86   HE_PROTOCOL,            // Received non-valid HTTP data
87   HE_DISCONNECTED,        // Connection closed unexpectedly
88   HE_OVERFLOW,            // Received too much data for internal buffers
89   HE_CONNECT_FAILED,      // The socket failed to connect.
90   HE_SOCKET_ERROR,        // An error occurred on a connected socket
91   HE_SHUTDOWN,            // Http object is being destroyed
92   HE_OPERATION_CANCELLED, // Connection aborted locally
93   HE_AUTH,                // Proxy Authentication Required
94   HE_CERTIFICATE_EXPIRED, // During SSL negotiation
95   HE_STREAM,              // Problem reading or writing to the document
96   HE_CACHE,               // Problem reading from cache
97   HE_DEFAULT
98 };
99 
100 enum HttpHeader {
101   HH_AGE,
102   HH_CACHE_CONTROL,
103   HH_CONNECTION,
104   HH_CONTENT_DISPOSITION,
105   HH_CONTENT_LENGTH,
106   HH_CONTENT_RANGE,
107   HH_CONTENT_TYPE,
108   HH_COOKIE,
109   HH_DATE,
110   HH_ETAG,
111   HH_EXPIRES,
112   HH_HOST,
113   HH_IF_MODIFIED_SINCE,
114   HH_IF_NONE_MATCH,
115   HH_KEEP_ALIVE,
116   HH_LAST_MODIFIED,
117   HH_LOCATION,
118   HH_PROXY_AUTHENTICATE,
119   HH_PROXY_AUTHORIZATION,
120   HH_PROXY_CONNECTION,
121   HH_RANGE,
122   HH_SET_COOKIE,
123   HH_TE,
124   HH_TRAILERS,
125   HH_TRANSFER_ENCODING,
126   HH_UPGRADE,
127   HH_USER_AGENT,
128   HH_WWW_AUTHENTICATE,
129   HH_LAST = HH_WWW_AUTHENTICATE
130 };
131 
132 const uint16 HTTP_DEFAULT_PORT = 80;
133 const uint16 HTTP_SECURE_PORT = 443;
134 
135 //////////////////////////////////////////////////////////////////////
136 // Utility Functions
137 //////////////////////////////////////////////////////////////////////
138 
139 inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) {
140   return (err != HE_NONE) ? err : def_err;
141 }
142 
143 const char* ToString(HttpVersion version);
144 bool FromString(HttpVersion& version, const std::string& str);
145 
146 const char* ToString(HttpVerb verb);
147 bool FromString(HttpVerb& verb, const std::string& str);
148 
149 const char* ToString(HttpHeader header);
150 bool FromString(HttpHeader& header, const std::string& str);
151 
HttpCodeIsInformational(uint32 code)152 inline bool HttpCodeIsInformational(uint32 code) { return ((code / 100) == 1); }
HttpCodeIsSuccessful(uint32 code)153 inline bool HttpCodeIsSuccessful(uint32 code)    { return ((code / 100) == 2); }
HttpCodeIsRedirection(uint32 code)154 inline bool HttpCodeIsRedirection(uint32 code)   { return ((code / 100) == 3); }
HttpCodeIsClientError(uint32 code)155 inline bool HttpCodeIsClientError(uint32 code)   { return ((code / 100) == 4); }
HttpCodeIsServerError(uint32 code)156 inline bool HttpCodeIsServerError(uint32 code)   { return ((code / 100) == 5); }
157 
158 bool HttpCodeHasBody(uint32 code);
159 bool HttpCodeIsCacheable(uint32 code);
160 bool HttpHeaderIsEndToEnd(HttpHeader header);
161 bool HttpHeaderIsCollapsible(HttpHeader header);
162 
163 struct HttpData;
164 bool HttpShouldKeepAlive(const HttpData& data);
165 
166 typedef std::pair<std::string, std::string> HttpAttribute;
167 typedef std::vector<HttpAttribute> HttpAttributeList;
168 void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
169                            std::string* composed);
170 void HttpParseAttributes(const char * data, size_t len,
171                          HttpAttributeList& attributes);
172 bool HttpHasAttribute(const HttpAttributeList& attributes,
173                       const std::string& name,
174                       std::string* value);
175 bool HttpHasNthAttribute(HttpAttributeList& attributes,
176                          size_t index,
177                          std::string* name,
178                          std::string* value);
179 
180 // Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp
181 bool HttpDateToSeconds(const std::string& date, time_t* seconds);
182 
HttpDefaultPort(bool secure)183 inline uint16 HttpDefaultPort(bool secure) {
184   return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT;
185 }
186 
187 // Returns the http server notation for a given address
188 std::string HttpAddress(const SocketAddress& address, bool secure);
189 
190 // functional for insensitive std::string compare
191 struct iless {
operatoriless192   bool operator()(const std::string& lhs, const std::string& rhs) const {
193     return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0);
194   }
195 };
196 
197 // put quotes around a string and escape any quotes inside it
198 std::string quote(const std::string& str);
199 
200 //////////////////////////////////////////////////////////////////////
201 // Url
202 //////////////////////////////////////////////////////////////////////
203 
204 template<class CTYPE>
205 class Url {
206 public:
207   typedef typename Traits<CTYPE>::string string;
208 
209   // TODO: Implement Encode/Decode
210   static int Encode(const CTYPE* source, CTYPE* destination, size_t len);
211   static int Encode(const string& source, string& destination);
212   static int Decode(const CTYPE* source, CTYPE* destination, size_t len);
213   static int Decode(const string& source, string& destination);
214 
Url(const string & url)215   Url(const string& url) { do_set_url(url.c_str(), url.size()); }
216   Url(const string& path, const string& host, uint16 port = HTTP_DEFAULT_PORT)
host_(host)217   : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port)
218   { set_full_path(path); }
219 
valid()220   bool valid() const { return !host_.empty(); }
clear()221   void clear() {
222     host_.clear();
223     port_ = HTTP_DEFAULT_PORT;
224     secure_ = false;
225     path_.assign(1, static_cast<CTYPE>('/'));
226     query_.clear();
227   }
228 
set_url(const string & val)229   void set_url(const string& val) {
230     do_set_url(val.c_str(), val.size());
231   }
url()232   string url() const {
233     string val; do_get_url(&val); return val;
234   }
235 
set_address(const string & val)236   void set_address(const string& val) {
237     do_set_address(val.c_str(), val.size());
238   }
address()239   string address() const {
240     string val; do_get_address(&val); return val;
241   }
242 
set_full_path(const string & val)243   void set_full_path(const string& val) {
244     do_set_full_path(val.c_str(), val.size());
245   }
full_path()246   string full_path() const {
247     string val; do_get_full_path(&val); return val;
248   }
249 
set_host(const string & val)250   void set_host(const string& val) { host_ = val; }
host()251   const string& host() const { return host_; }
252 
set_port(uint16 val)253   void set_port(uint16 val) { port_ = val; }
port()254   uint16 port() const { return port_; }
255 
set_secure(bool val)256   void set_secure(bool val) { secure_ = val; }
secure()257   bool secure() const { return secure_; }
258 
set_path(const string & val)259   void set_path(const string& val) {
260     if (val.empty()) {
261       path_.assign(1, static_cast<CTYPE>('/'));
262     } else {
263       ASSERT(val[0] == static_cast<CTYPE>('/'));
264       path_ = val;
265     }
266   }
path()267   const string& path() const { return path_; }
268 
set_query(const string & val)269   void set_query(const string& val) {
270     ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('?')));
271     query_ = val;
272   }
query()273   const string& query() const { return query_; }
274 
275   bool get_attribute(const string& name, string* value) const;
276 
277 private:
278   void do_set_url(const CTYPE* val, size_t len);
279   void do_set_address(const CTYPE* val, size_t len);
280   void do_set_full_path(const CTYPE* val, size_t len);
281 
282   void do_get_url(string* val) const;
283   void do_get_address(string* val) const;
284   void do_get_full_path(string* val) const;
285 
286   string host_, path_, query_;
287   uint16 port_;
288   bool secure_;
289 };
290 
291 //////////////////////////////////////////////////////////////////////
292 // HttpData
293 //////////////////////////////////////////////////////////////////////
294 
295 struct HttpData {
296   typedef std::multimap<std::string, std::string, iless> HeaderMap;
297   typedef HeaderMap::const_iterator const_iterator;
298   typedef HeaderMap::iterator iterator;
299 
300   HttpVersion version;
301   scoped_ptr<StreamInterface> document;
302 
HttpDataHttpData303   HttpData() : version(HVER_1_1) { }
304 
305   enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW };
306   void changeHeader(const std::string& name, const std::string& value,
307                     HeaderCombine combine);
308   inline void addHeader(const std::string& name, const std::string& value,
309                         bool append = true) {
310     changeHeader(name, value, append ? HC_AUTO : HC_NO);
311   }
312   inline void setHeader(const std::string& name, const std::string& value,
313                         bool overwrite = true) {
314     changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW);
315   }
316   // Returns count of erased headers
317   size_t clearHeader(const std::string& name);
318   // Returns iterator to next header
319   iterator clearHeader(iterator header);
320 
321   // keep in mind, this may not do what you want in the face of multiple headers
322   bool hasHeader(const std::string& name, std::string* value) const;
323 
beginHttpData324   inline const_iterator begin() const {
325     return headers_.begin();
326   }
endHttpData327   inline const_iterator end() const {
328     return headers_.end();
329   }
beginHttpData330   inline iterator begin() {
331     return headers_.begin();
332   }
endHttpData333   inline iterator end() {
334     return headers_.end();
335   }
beginHttpData336   inline const_iterator begin(const std::string& name) const {
337     return headers_.lower_bound(name);
338   }
endHttpData339   inline const_iterator end(const std::string& name) const {
340     return headers_.upper_bound(name);
341   }
beginHttpData342   inline iterator begin(const std::string& name) {
343     return headers_.lower_bound(name);
344   }
endHttpData345   inline iterator end(const std::string& name) {
346     return headers_.upper_bound(name);
347   }
348 
349   // Convenience methods using HttpHeader
changeHeaderHttpData350   inline void changeHeader(HttpHeader header, const std::string& value,
351                            HeaderCombine combine) {
352     changeHeader(ToString(header), value, combine);
353   }
354   inline void addHeader(HttpHeader header, const std::string& value,
355                         bool append = true) {
356     addHeader(ToString(header), value, append);
357   }
358   inline void setHeader(HttpHeader header, const std::string& value,
359                         bool overwrite = true) {
360     setHeader(ToString(header), value, overwrite);
361   }
clearHeaderHttpData362   inline void clearHeader(HttpHeader header) {
363     clearHeader(ToString(header));
364   }
hasHeaderHttpData365   inline bool hasHeader(HttpHeader header, std::string* value) const {
366     return hasHeader(ToString(header), value);
367   }
beginHttpData368   inline const_iterator begin(HttpHeader header) const {
369     return headers_.lower_bound(ToString(header));
370   }
endHttpData371   inline const_iterator end(HttpHeader header) const {
372     return headers_.upper_bound(ToString(header));
373   }
beginHttpData374   inline iterator begin(HttpHeader header) {
375     return headers_.lower_bound(ToString(header));
376   }
endHttpData377   inline iterator end(HttpHeader header) {
378     return headers_.upper_bound(ToString(header));
379   }
380 
381   void setContent(const std::string& content_type, StreamInterface* document);
382   void setDocumentAndLength(StreamInterface* document);
383 
384   virtual size_t formatLeader(char* buffer, size_t size) const = 0;
385   virtual HttpError parseLeader(const char* line, size_t len) = 0;
386 
387 protected:
~HttpDataHttpData388   virtual ~HttpData() { }
389   void clear(bool release_document);
390   void copy(const HttpData& src);
391 
392 private:
393   HeaderMap headers_;
394 };
395 
396 struct HttpRequestData : public HttpData {
397   HttpVerb verb;
398   std::string path;
399 
HttpRequestDataHttpRequestData400   HttpRequestData() : verb(HV_GET) { }
401 
402   void clear(bool release_document);
403   void copy(const HttpRequestData& src);
404 
405   virtual size_t formatLeader(char* buffer, size_t size) const;
406   virtual HttpError parseLeader(const char* line, size_t len);
407 
408   bool getAbsoluteUri(std::string* uri) const;
409   bool getRelativeUri(std::string* host, std::string* path) const;
410 };
411 
412 struct HttpResponseData : public HttpData {
413   uint32 scode;
414   std::string message;
415 
HttpResponseDataHttpResponseData416   HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { }
417   void clear(bool release_document);
418   void copy(const HttpResponseData& src);
419 
420   // Convenience methods
421   void set_success(uint32 scode = HC_OK);
422   void set_success(const std::string& content_type, StreamInterface* document,
423                    uint32 scode = HC_OK);
424   void set_redirect(const std::string& location,
425                     uint32 scode = HC_MOVED_TEMPORARILY);
426   void set_error(uint32 scode);
427 
428   virtual size_t formatLeader(char* buffer, size_t size) const;
429   virtual HttpError parseLeader(const char* line, size_t len);
430 };
431 
432 struct HttpTransaction {
433   HttpRequestData request;
434   HttpResponseData response;
435 };
436 
437 //////////////////////////////////////////////////////////////////////
438 // Http Authentication
439 //////////////////////////////////////////////////////////////////////
440 
441 struct HttpAuthContext {
442   std::string auth_method;
HttpAuthContextHttpAuthContext443   HttpAuthContext(const std::string& auth) : auth_method(auth) { }
~HttpAuthContextHttpAuthContext444   virtual ~HttpAuthContext() { }
445 };
446 
447 enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR };
448 
449 // 'context' is used by this function to record information between calls.
450 // Start by passing a null pointer, then pass the same pointer each additional
451 // call.  When the authentication attempt is finished, delete the context.
452 HttpAuthResult HttpAuthenticate(
453   const char * challenge, size_t len,
454   const SocketAddress& server,
455   const std::string& method, const std::string& uri,
456   const std::string& username, const CryptString& password,
457   HttpAuthContext *& context, std::string& response, std::string& auth_method);
458 
459 //////////////////////////////////////////////////////////////////////
460 
461 } // namespace talk_base
462 
463 #endif // TALK_BASE_HTTPCOMMON_H__
464