PageRenderTime 187ms CodeModel.GetById 101ms app.highlight 46ms RepoModel.GetById 34ms app.codeStats 1ms

/mordor/http/http.h

http://github.com/mozy/mordor
C Header | 535 lines | 420 code | 79 blank | 36 comment | 39 complexity | e2c95befba66ac5eddd990a635800ec7 MD5 | raw file
  1#ifndef __MORDOR_HTTP_H__
  2#define __MORDOR_HTTP_H__
  3// Copyright (c) 2009 - Mozy, Inc.
  4
  5#include <map>
  6#include <set>
  7#include <stdexcept>
  8#include <vector>
  9
 10#include <boost/variant.hpp>
 11#include <boost/date_time.hpp>
 12
 13#include "mordor/predef.h"
 14#include "mordor/string.h"
 15#include "mordor/uri.h"
 16#include "mordor/version.h"
 17
 18namespace Mordor {
 19namespace HTTP {
 20
 21struct Exception : virtual Mordor::Exception {};
 22struct IncompleteMessageHeaderException : virtual Exception, virtual StreamException {};
 23
 24// Unparseable
 25struct BadMessageHeaderException : virtual Exception, StreamException {};
 26
 27struct PriorRequestFailedException : virtual Exception {};
 28
 29struct ConnectionVoluntarilyClosedException : virtual PriorRequestFailedException {};
 30
 31// Logically doesn't make sense
 32struct InvalidMessageHeaderException : virtual Exception, virtual StreamException {
 33public:
 34    InvalidMessageHeaderException() {}
 35    InvalidMessageHeaderException(const std::string &message)
 36        : m_message(message) {}
 37    ~InvalidMessageHeaderException() throw() {}
 38
 39    const char *what() const throw() { return m_message.c_str(); }
 40private:
 41    std::string m_message;
 42};
 43
 44struct InvalidTransferEncodingException : virtual InvalidMessageHeaderException
 45{
 46public:
 47    InvalidTransferEncodingException(const std::string &message)
 48        : InvalidMessageHeaderException(message)
 49    {}
 50};
 51
 52extern const std::string GET;
 53extern const std::string HEAD;
 54extern const std::string POST;
 55extern const std::string PUT;
 56extern const std::string DELETE;
 57extern const std::string CONNECT;
 58extern const std::string OPTIONS;
 59extern const std::string TRACE;
 60
 61enum Status
 62{
 63    INVALID                          = 0,
 64
 65    CONTINUE                         = 100,
 66    SWITCHING_PROTOCOL               = 101,
 67
 68    OK                               = 200,
 69    CREATED                          = 201,
 70    ACCEPTED                         = 202,
 71    NON_AUTHORITATIVE_INFORMATION    = 203,
 72    NO_CONTENT                       = 204,
 73    RESET_CONTENT                    = 205,
 74    PARTIAL_CONTENT                  = 206,
 75
 76    MULTIPLE_CHOICES                 = 300,
 77    MOVED_PERMANENTLY                = 301,
 78    FOUND                            = 302,
 79    SEE_OTHER                        = 303,
 80    NOT_MODIFIED                     = 304,
 81    USE_PROXY                        = 305,
 82    // UNUSED                        = 306,
 83    TEMPORARY_REDIRECT               = 307,
 84
 85    BAD_REQUEST                      = 400,
 86    UNAUTHORIZED                     = 401,
 87    PAYMENT_REQUIRED                 = 402,
 88    FORBIDDEN                        = 403,
 89    NOT_FOUND                        = 404,
 90    METHOD_NOT_ALLOWED               = 405,
 91    NOT_ACCEPTABLE                   = 406,
 92    PROXY_AUTHENTICATION_REQUIRED    = 407,
 93    REQUEST_TIMEOUT                  = 408,
 94    CONFLICT                         = 409,
 95    GONE                             = 410,
 96    LENGTH_REQUIRED                  = 411,
 97    PRECONDITION_FAILED              = 412,
 98    REQUEST_ENTITY_TOO_LARGE         = 413,
 99    REQUEST_URI_TOO_LONG             = 414,
100    UNSUPPORTED_MEDIA_TYPE           = 415,
101    REQUESTED_RANGE_NOT_SATISFIABLE  = 416,
102    EXPECTATION_FAILED               = 417,
103    PRECONDITION_REQUIRED            = 428,
104
105    INTERNAL_SERVER_ERROR            = 500,
106    NOT_IMPLEMENTED                  = 501,
107    BAD_GATEWAY                      = 502,
108    SERVICE_UNAVAILABLE              = 503,
109    GATEWAY_TIMEOUT                  = 504,
110    HTTP_VERSION_NOT_SUPPORTED       = 505
111};
112const char *reason(Status s);
113
114class Redirect : public HTTP::Exception
115{
116public:
117    Redirect(Status status, const URI &uri)
118        : m_status(status), m_uri(uri)
119    {}
120
121    ~Redirect() throw() {}
122
123    Status status() const { return m_status; }
124    const URI &uri() const { return m_uri; }
125
126private:
127    Status m_status;
128    URI m_uri;
129};
130
131std::string quote(const std::string &str, bool alwaysQuote = false, bool comment = false);
132std::string unquote(const char *str, size_t size);
133std::string unquote(const std::string &str);
134boost::posix_time::ptime parseHttpDate(const char *str, size_t size);
135
136// Mordor uses the following datastrutures to represent the standard contents
137// of HTTP headers in a C++-friendly fashion.  These are used to build and
138// parse HTTP requests and responses.
139
140struct Version
141{
142    Version() : major(~0), minor(~0) {}
143    Version(unsigned char m, unsigned char n) : major(m), minor(n) {}
144
145    unsigned char major;
146    unsigned char minor;
147
148    bool operator==(const Version& rhs) const
149    {
150        return major == rhs.major && minor == rhs.minor;
151    }
152    bool operator!=(const Version& rhs) const
153    {
154        return !(*this == rhs);
155    }
156    bool operator<(const Version& rhs) const
157    {
158        return major < rhs.major || (major == rhs.major && minor < rhs.minor);
159    }
160    bool operator<=(const Version& rhs) const
161    {
162        return !(*this > rhs);
163    }
164    bool operator>(const Version& rhs) const
165    {
166        return rhs < *this;
167    }
168    bool operator>=(const Version& rhs) const
169    {
170        return !(*this < rhs);
171    }
172};
173
174struct ETag
175{
176    ETag()
177        : weak(false), unspecified(true)
178    {}
179    ETag(const std::string &v, bool w = false)
180        : weak(w), unspecified(false), value(v)
181    {}
182
183    bool weak, unspecified;
184    std::string value;
185
186    bool operator< (const ETag &rhs) const
187    {
188        if (unspecified && !rhs.unspecified)
189            return true;
190        if (!unspecified && rhs.unspecified)
191            return false;
192        if (weak && !rhs.weak)
193            return false;
194        if (!weak && rhs.weak)
195            return true;
196        return value < rhs.value;
197    }
198
199    /// Compare two Entity Tags according to the weak comparison function
200    ///
201    /// @return if *this and rhs have the same value, ignoring weakness
202    bool weakCompare(const ETag &rhs) const
203    {
204        return unspecified == rhs.unspecified && value == rhs.value;
205    }
206
207    /// Compare two Entity Tags according to the strong comparison function
208    ///
209    /// @return if *this and rhs are both strong, and have the same value
210    bool strongCompare(const ETag &rhs) const
211    {
212        return !weak && !rhs.weak && unspecified == rhs.unspecified &&
213            value == rhs.value;
214    }
215
216    /// Compare two Entity Tags for exact equality
217    ///
218    /// @return if *this and rhs are identical (weakness and value)
219    bool operator== (const ETag &rhs) const
220    {
221        return weak == rhs.weak && unspecified == rhs.unspecified &&
222            value == rhs.value;
223    }
224
225    bool operator!= (const ETag &rhs) const
226    {
227        return weak != rhs.weak || unspecified != rhs.unspecified ||
228            value != rhs.value;
229    }
230};
231
232struct Product
233{
234    Product() {}
235    Product(const std::string &_product, const std::string &_version)
236        : product(_product), version(_version)
237    {}
238    Product(const char *_product, const char *_version)
239        : product(_product), version(_version)
240    {}
241    std::string product;
242    std::string version;
243};
244
245typedef std::vector<Product> ProductList;
246typedef std::vector<boost::variant<Product, std::string> > ProductAndCommentList;
247
248typedef std::set<std::string, caseinsensitiveless> StringSet;
249typedef std::map<std::string, std::string, caseinsensitiveless> StringMap;
250
251struct ValueWithParameters
252{
253    ValueWithParameters()
254    {}
255    ValueWithParameters(const char *val) : value(val)
256    {}
257    ValueWithParameters(const std::string &val) : value(val)
258    {}
259
260    std::string value;
261    StringMap parameters;
262};
263
264typedef std::vector<ValueWithParameters> ParameterizedList;
265
266struct AuthParams
267{
268    AuthParams(const std::string &_scheme = std::string(),
269        const std::string &_param = std::string())
270        : scheme(_scheme),
271          param(_param)
272    {}
273    std::string scheme;
274    /// non-list parameters
275    std::string param;
276    /// key=value pair list parameters
277    StringMap parameters;
278};
279
280typedef std::vector<AuthParams> ChallengeList;
281
282struct KeyValueWithParameters
283{
284    KeyValueWithParameters()
285    {}
286    KeyValueWithParameters(const std::string &_key,
287        const std::string &_value)
288        : key(_key), value(_value)
289    {}
290    KeyValueWithParameters(const char *_key)
291        : key(_key)
292    {}
293    KeyValueWithParameters(const char *_key,
294        const char *_value)
295        : key(_key), value(_value)
296    {}
297
298    std::string key;
299    std::string value;
300    StringMap parameters;
301};
302
303typedef std::vector<KeyValueWithParameters> ParameterizedKeyValueList;
304
305struct MediaType
306{
307    MediaType() {}
308    MediaType(const std::string &type_, const std::string &subtype_)
309        : type(type_), subtype(subtype_)
310    {}
311
312    std::string type;
313    std::string subtype;
314    StringMap parameters;
315};
316
317typedef std::vector<std::pair<unsigned long long, unsigned long long> > RangeSet;
318struct ContentRange
319{
320    ContentRange(unsigned long long first_ = ~0ull,
321        unsigned long long last_ = ~0ull,
322        unsigned long long instance_ = ~0ull)
323        : first(first_),
324          last(last_),
325          instance(instance_)
326    {}
327
328    unsigned long long first;
329    /// @note If first == ~0ull, then last is ignored for comparison, and only
330    /// useful for forcing a serialization of "bytes */*"
331    unsigned long long last;
332    unsigned long long instance;
333
334    bool operator==(const ContentRange &rhs) const
335    {
336        return first == rhs.first && (first == ~0ull || last == rhs.last) &&
337            instance == rhs.instance;
338    }
339    bool operator!=(const ContentRange &rhs) const
340    {
341        return !(*this == rhs);
342    }
343};
344
345struct AcceptValue
346{
347    AcceptValue() : qvalue(~0u) {}
348    AcceptValue(const char *v, unsigned int q = ~0u)
349        : value(v), qvalue(q)
350    {}
351    AcceptValue(const std::string &v, unsigned int q = ~0u)
352        : value(v), qvalue(q)
353    {}
354
355    std::string value;
356    unsigned int qvalue;
357
358    bool operator== (const AcceptValue &rhs) const;
359    bool operator!= (const AcceptValue &rhs) const { return !(*this == rhs); }
360};
361
362typedef std::vector<AcceptValue> AcceptList;
363
364struct AcceptValueWithParameters
365{
366    AcceptValueWithParameters() : qvalue(~0u) {}
367    AcceptValueWithParameters(const char *v, unsigned int q = ~0u)
368        : value(v), qvalue(q)
369    {}
370    AcceptValueWithParameters(const std::string &v, unsigned int q = ~0u)
371        : value(v), qvalue(q)
372    {}
373
374    std::string value;
375    StringMap parameters;
376    unsigned int qvalue;
377    StringMap acceptParams;
378
379    bool operator== (const AcceptValueWithParameters &rhs) const;
380    bool operator!= (const AcceptValueWithParameters &rhs) const
381    { return !(*this == rhs); }
382};
383
384typedef std::vector<AcceptValueWithParameters> AcceptListWithParameters;
385
386// First line of a HTTP Request, e.g. "GET /events/1196798 HTTP/1.1"
387struct RequestLine
388{
389    RequestLine() : method(GET) {}
390
391    std::string method;
392    URI uri;
393    Version ver;
394};
395
396
397// First line of an HTTP Response, e.g. "HTTP/1.1 200 OK"
398struct StatusLine
399{
400    StatusLine() : status(OK) {}
401
402    Status status;
403    std::string reason;
404    Version ver;
405};
406
407// Each member of the following structures contains the parsed value
408// of a standard HTTP header of approximately the same name.
409// e.g. GeneralHeaders::connection contains the "Connection" header
410// and RequestHeaders::acceptCharset contains the "Accept-Charset" header
411// (see http://en.wikipedia.org/wiki/List_of_HTTP_header_fields)
412
413struct GeneralHeaders
414{
415    StringSet connection;
416    boost::posix_time::ptime date;
417    StringSet proxyConnection; // NON-STANDARD!!!!
418    ParameterizedList transferEncoding;
419    StringSet trailer;
420    ProductList upgrade;
421};
422
423struct RequestHeaders
424{
425    AcceptList acceptCharset;
426    AcceptList acceptEncoding;
427    AuthParams authorization;
428    ParameterizedKeyValueList expect;
429    std::string host;
430    std::set<ETag> ifMatch;
431    boost::posix_time::ptime ifModifiedSince;
432    std::set<ETag> ifNoneMatch;
433    boost::variant<ETag, boost::posix_time::ptime> ifRange;
434    boost::posix_time::ptime ifUnmodifiedSince;
435    AuthParams proxyAuthorization;
436    RangeSet range;
437    URI referer;
438    AcceptListWithParameters te;
439    ProductAndCommentList userAgent;
440};
441
442struct ResponseHeaders
443{
444    StringSet acceptRanges;
445    ETag eTag;
446    URI location;
447    ChallengeList proxyAuthenticate;
448    boost::variant<boost::posix_time::ptime, unsigned long long> retryAfter;
449    ProductAndCommentList server;
450    ChallengeList wwwAuthenticate;
451};
452
453struct EntityHeaders
454{
455    EntityHeaders() : contentLength(~0ull) {}
456
457    std::vector<std::string> allow; // "Allow"
458    std::vector<std::string> contentEncoding; // "Content-Encoding"
459    unsigned long long contentLength;         // "Content-Length"
460    std::string contentMD5;                   // "Content-MD5"
461    ContentRange contentRange;                // "Content-Range"
462    MediaType contentType;                    // "Content-Type"
463    boost::posix_time::ptime expires;         // "Expires"
464    boost::posix_time::ptime lastModified;    // "Last-Modified"
465
466    // All non-standard headers are stored in this map.
467    // Typically these headers use the naming convention "X-...",
468    // for example "X-Meta", "X-ObjectID" but that is not manditory.
469    // This is also the structure to set any Cookie
470    StringMap extension;
471};
472
473struct Request
474{
475    RequestLine requestLine;
476    GeneralHeaders general;
477    RequestHeaders request;
478    EntityHeaders entity;
479};
480
481struct Response
482{
483    StatusLine status;
484    GeneralHeaders general;
485    ResponseHeaders response;
486    EntityHeaders entity;
487};
488
489bool isAcceptable(const ChallengeList &list, const std::string &scheme);
490const AuthParams &challengeForSchemeAndRealm(const ChallengeList &list,
491    const std::string &scheme, const std::string &realm = std::string());
492
493bool isAcceptable(const AcceptListWithParameters &list, const AcceptValueWithParameters &value, bool defaultMissing = false);
494// @note the available MUST be sorted in descending order before sending to this function
495const AcceptValueWithParameters *preferred(const AcceptListWithParameters &accept, const AcceptListWithParameters &available);
496
497bool isAcceptable(const AcceptList &list, const AcceptValue &value, bool defaultMissing = false);
498// @note the available MUST be sorted in descending order before sending to this function
499const AcceptValue *preferred(const AcceptList &accept, const AcceptList &available);
500
501std::ostream& operator<<(std::ostream& os, Status s);
502std::ostream& operator<<(std::ostream& os, Version v);
503std::ostream& operator<<(std::ostream& os, const ETag &e);
504std::ostream& operator<<(std::ostream& os, const std::set<ETag> &v);
505std::ostream& operator<<(std::ostream& os, const Product &p);
506std::ostream& operator<<(std::ostream& os, const ProductList &l);
507std::ostream& operator<<(std::ostream& os, const ProductAndCommentList &l);
508std::ostream& operator<<(std::ostream& os, const ValueWithParameters &v);
509std::ostream& operator<<(std::ostream& os, const ParameterizedList &l);
510std::ostream& operator<<(std::ostream& os, const AuthParams &v);
511std::ostream& operator<<(std::ostream& os, const ChallengeList &l);
512std::ostream& operator<<(std::ostream& os, const KeyValueWithParameters &v);
513std::ostream& operator<<(std::ostream& os, const ParameterizedKeyValueList &v);
514std::ostream& operator<<(std::ostream& os, const MediaType &m);
515std::ostream& operator<<(std::ostream& os, const ContentRange &m);
516std::ostream& operator<<(std::ostream& os, const AcceptValue &v);
517std::ostream& operator<<(std::ostream& os, const AcceptList &l);
518std::ostream& operator<<(std::ostream& os, const AcceptValueWithParameters &v);
519std::ostream& operator<<(std::ostream& os, const AcceptListWithParameters &l);
520std::ostream& operator<<(std::ostream& os, const RequestLine &r);
521std::ostream& operator<<(std::ostream& os, const StatusLine &s);
522std::ostream& operator<<(std::ostream& os, const GeneralHeaders &g);
523std::ostream& operator<<(std::ostream& os, const RequestHeaders &r);
524std::ostream& operator<<(std::ostream& os, const ResponseHeaders &r);
525std::ostream& operator<<(std::ostream& os, const EntityHeaders &e);
526
527// These operators are used to convert the Request and Response
528// structures, as filled in by a Client or Server, into the real
529// HTTP string format that is sent "over the wire"
530std::ostream& operator<<(std::ostream& os, const Request &r);
531std::ostream& operator<<(std::ostream& os, const Response &r);
532
533}}
534
535#endif