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, unsigned long* 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