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
40namespace talk_base {
41
42class CryptString;
43class SocketAddress;
44
45//////////////////////////////////////////////////////////////////////
46// Constants
47//////////////////////////////////////////////////////////////////////
48
49enum 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
74enum HttpVersion {
75  HVER_1_0, HVER_1_1, HVER_UNKNOWN,
76  HVER_LAST = HVER_UNKNOWN
77};
78
79enum HttpVerb {
80  HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD,
81  HV_LAST = HV_HEAD
82};
83
84enum 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
100enum 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
132const uint16 HTTP_DEFAULT_PORT = 80;
133const uint16 HTTP_SECURE_PORT = 443;
134
135//////////////////////////////////////////////////////////////////////
136// Utility Functions
137//////////////////////////////////////////////////////////////////////
138
139inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) {
140  return (err != HE_NONE) ? err : def_err;
141}
142
143const char* ToString(HttpVersion version);
144bool FromString(HttpVersion& version, const std::string& str);
145
146const char* ToString(HttpVerb verb);
147bool FromString(HttpVerb& verb, const std::string& str);
148
149const char* ToString(HttpHeader header);
150bool FromString(HttpHeader& header, const std::string& str);
151
152inline bool HttpCodeIsInformational(uint32 code) { return ((code / 100) == 1); }
153inline bool HttpCodeIsSuccessful(uint32 code)    { return ((code / 100) == 2); }
154inline bool HttpCodeIsRedirection(uint32 code)   { return ((code / 100) == 3); }
155inline bool HttpCodeIsClientError(uint32 code)   { return ((code / 100) == 4); }
156inline bool HttpCodeIsServerError(uint32 code)   { return ((code / 100) == 5); }
157
158bool HttpCodeHasBody(uint32 code);
159bool HttpCodeIsCacheable(uint32 code);
160bool HttpHeaderIsEndToEnd(HttpHeader header);
161bool HttpHeaderIsCollapsible(HttpHeader header);
162
163struct HttpData;
164bool HttpShouldKeepAlive(const HttpData& data);
165
166typedef std::pair<std::string, std::string> HttpAttribute;
167typedef std::vector<HttpAttribute> HttpAttributeList;
168void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
169                           std::string* composed);
170void HttpParseAttributes(const char * data, size_t len,
171                         HttpAttributeList& attributes);
172bool HttpHasAttribute(const HttpAttributeList& attributes,
173                      const std::string& name,
174                      std::string* value);
175bool 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
181bool HttpDateToSeconds(const std::string& date, time_t* seconds);
182
183inline 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
188std::string HttpAddress(const SocketAddress& address, bool secure);
189
190// functional for insensitive std::string compare
191struct iless {
192  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
198std::string quote(const std::string& str);
199
200//////////////////////////////////////////////////////////////////////
201// Url
202//////////////////////////////////////////////////////////////////////
203
204template<class CTYPE>
205class Url {
206public:
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
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)
217  : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port)
218  { set_full_path(path); }
219
220  bool valid() const { return !host_.empty(); }
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
229  void set_url(const string& val) {
230    do_set_url(val.c_str(), val.size());
231  }
232  string url() const {
233    string val; do_get_url(&val); return val;
234  }
235
236  void set_address(const string& val) {
237    do_set_address(val.c_str(), val.size());
238  }
239  string address() const {
240    string val; do_get_address(&val); return val;
241  }
242
243  void set_full_path(const string& val) {
244    do_set_full_path(val.c_str(), val.size());
245  }
246  string full_path() const {
247    string val; do_get_full_path(&val); return val;
248  }
249
250  void set_host(const string& val) { host_ = val; }
251  const string& host() const { return host_; }
252
253  void set_port(uint16 val) { port_ = val; }
254  uint16 port() const { return port_; }
255
256  void set_secure(bool val) { secure_ = val; }
257  bool secure() const { return secure_; }
258
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  }
267  const string& path() const { return path_; }
268
269  void set_query(const string& val) {
270    ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('?')));
271    query_ = val;
272  }
273  const string& query() const { return query_; }
274
275  bool get_attribute(const string& name, string* value) const;
276
277private:
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
295struct 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
303  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
324  inline const_iterator begin() const {
325    return headers_.begin();
326  }
327  inline const_iterator end() const {
328    return headers_.end();
329  }
330  inline iterator begin() {
331    return headers_.begin();
332  }
333  inline iterator end() {
334    return headers_.end();
335  }
336  inline const_iterator begin(const std::string& name) const {
337    return headers_.lower_bound(name);
338  }
339  inline const_iterator end(const std::string& name) const {
340    return headers_.upper_bound(name);
341  }
342  inline iterator begin(const std::string& name) {
343    return headers_.lower_bound(name);
344  }
345  inline iterator end(const std::string& name) {
346    return headers_.upper_bound(name);
347  }
348
349  // Convenience methods using HttpHeader
350  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  }
362  inline void clearHeader(HttpHeader header) {
363    clearHeader(ToString(header));
364  }
365  inline bool hasHeader(HttpHeader header, std::string* value) const {
366    return hasHeader(ToString(header), value);
367  }
368  inline const_iterator begin(HttpHeader header) const {
369    return headers_.lower_bound(ToString(header));
370  }
371  inline const_iterator end(HttpHeader header) const {
372    return headers_.upper_bound(ToString(header));
373  }
374  inline iterator begin(HttpHeader header) {
375    return headers_.lower_bound(ToString(header));
376  }
377  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
387protected:
388  virtual ~HttpData() { }
389  void clear(bool release_document);
390  void copy(const HttpData& src);
391
392private:
393  HeaderMap headers_;
394};
395
396struct HttpRequestData : public HttpData {
397  HttpVerb verb;
398  std::string path;
399
400  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
412struct HttpResponseData : public HttpData {
413  uint32 scode;
414  std::string message;
415
416  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
432struct HttpTransaction {
433  HttpRequestData request;
434  HttpResponseData response;
435};
436
437//////////////////////////////////////////////////////////////////////
438// Http Authentication
439//////////////////////////////////////////////////////////////////////
440
441struct HttpAuthContext {
442  std::string auth_method;
443  HttpAuthContext(const std::string& auth) : auth_method(auth) { }
444  virtual ~HttpAuthContext() { }
445};
446
447enum 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.
452HttpAuthResult 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