|  | /* | 
|  | *  Copyright 2004 The WebRTC Project Authors. All rights reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #ifndef WEBRTC_BASE_HTTPCOMMON_H__ | 
|  | #define WEBRTC_BASE_HTTPCOMMON_H__ | 
|  |  | 
|  | #include <map> | 
|  | #include <string> | 
|  | #include <vector> | 
|  | #include "webrtc/base/basictypes.h" | 
|  | #include "webrtc/base/common.h" | 
|  | #include "webrtc/base/scoped_ptr.h" | 
|  | #include "webrtc/base/stringutils.h" | 
|  | #include "webrtc/base/stream.h" | 
|  |  | 
|  | namespace rtc { | 
|  |  | 
|  | class CryptString; | 
|  | class SocketAddress; | 
|  |  | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  | // Constants | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | enum HttpCode { | 
|  | HC_OK = 200, | 
|  | HC_NON_AUTHORITATIVE = 203, | 
|  | HC_NO_CONTENT = 204, | 
|  | HC_PARTIAL_CONTENT = 206, | 
|  |  | 
|  | HC_MULTIPLE_CHOICES = 300, | 
|  | HC_MOVED_PERMANENTLY = 301, | 
|  | HC_FOUND = 302, | 
|  | HC_SEE_OTHER = 303, | 
|  | HC_NOT_MODIFIED = 304, | 
|  | HC_MOVED_TEMPORARILY = 307, | 
|  |  | 
|  | HC_BAD_REQUEST = 400, | 
|  | HC_UNAUTHORIZED = 401, | 
|  | HC_FORBIDDEN = 403, | 
|  | HC_NOT_FOUND = 404, | 
|  | HC_PROXY_AUTHENTICATION_REQUIRED = 407, | 
|  | HC_GONE = 410, | 
|  |  | 
|  | HC_INTERNAL_SERVER_ERROR = 500, | 
|  | HC_NOT_IMPLEMENTED = 501, | 
|  | HC_SERVICE_UNAVAILABLE = 503, | 
|  | }; | 
|  |  | 
|  | enum HttpVersion { | 
|  | HVER_1_0, HVER_1_1, HVER_UNKNOWN, | 
|  | HVER_LAST = HVER_UNKNOWN | 
|  | }; | 
|  |  | 
|  | enum HttpVerb { | 
|  | HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD, | 
|  | HV_LAST = HV_HEAD | 
|  | }; | 
|  |  | 
|  | enum HttpError { | 
|  | HE_NONE, | 
|  | HE_PROTOCOL,            // Received non-valid HTTP data | 
|  | HE_DISCONNECTED,        // Connection closed unexpectedly | 
|  | HE_OVERFLOW,            // Received too much data for internal buffers | 
|  | HE_CONNECT_FAILED,      // The socket failed to connect. | 
|  | HE_SOCKET_ERROR,        // An error occurred on a connected socket | 
|  | HE_SHUTDOWN,            // Http object is being destroyed | 
|  | HE_OPERATION_CANCELLED, // Connection aborted locally | 
|  | HE_AUTH,                // Proxy Authentication Required | 
|  | HE_CERTIFICATE_EXPIRED, // During SSL negotiation | 
|  | HE_STREAM,              // Problem reading or writing to the document | 
|  | HE_CACHE,               // Problem reading from cache | 
|  | HE_DEFAULT | 
|  | }; | 
|  |  | 
|  | enum HttpHeader { | 
|  | HH_AGE, | 
|  | HH_CACHE_CONTROL, | 
|  | HH_CONNECTION, | 
|  | HH_CONTENT_DISPOSITION, | 
|  | HH_CONTENT_LENGTH, | 
|  | HH_CONTENT_RANGE, | 
|  | HH_CONTENT_TYPE, | 
|  | HH_COOKIE, | 
|  | HH_DATE, | 
|  | HH_ETAG, | 
|  | HH_EXPIRES, | 
|  | HH_HOST, | 
|  | HH_IF_MODIFIED_SINCE, | 
|  | HH_IF_NONE_MATCH, | 
|  | HH_KEEP_ALIVE, | 
|  | HH_LAST_MODIFIED, | 
|  | HH_LOCATION, | 
|  | HH_PROXY_AUTHENTICATE, | 
|  | HH_PROXY_AUTHORIZATION, | 
|  | HH_PROXY_CONNECTION, | 
|  | HH_RANGE, | 
|  | HH_SET_COOKIE, | 
|  | HH_TE, | 
|  | HH_TRAILERS, | 
|  | HH_TRANSFER_ENCODING, | 
|  | HH_UPGRADE, | 
|  | HH_USER_AGENT, | 
|  | HH_WWW_AUTHENTICATE, | 
|  | HH_LAST = HH_WWW_AUTHENTICATE | 
|  | }; | 
|  |  | 
|  | const uint16 HTTP_DEFAULT_PORT = 80; | 
|  | const uint16 HTTP_SECURE_PORT = 443; | 
|  |  | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  | // Utility Functions | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) { | 
|  | return (err != HE_NONE) ? err : def_err; | 
|  | } | 
|  |  | 
|  | const char* ToString(HttpVersion version); | 
|  | bool FromString(HttpVersion& version, const std::string& str); | 
|  |  | 
|  | const char* ToString(HttpVerb verb); | 
|  | bool FromString(HttpVerb& verb, const std::string& str); | 
|  |  | 
|  | const char* ToString(HttpHeader header); | 
|  | bool FromString(HttpHeader& header, const std::string& str); | 
|  |  | 
|  | inline bool HttpCodeIsInformational(uint32 code) { return ((code / 100) == 1); } | 
|  | inline bool HttpCodeIsSuccessful(uint32 code)    { return ((code / 100) == 2); } | 
|  | inline bool HttpCodeIsRedirection(uint32 code)   { return ((code / 100) == 3); } | 
|  | inline bool HttpCodeIsClientError(uint32 code)   { return ((code / 100) == 4); } | 
|  | inline bool HttpCodeIsServerError(uint32 code)   { return ((code / 100) == 5); } | 
|  |  | 
|  | bool HttpCodeHasBody(uint32 code); | 
|  | bool HttpCodeIsCacheable(uint32 code); | 
|  | bool HttpHeaderIsEndToEnd(HttpHeader header); | 
|  | bool HttpHeaderIsCollapsible(HttpHeader header); | 
|  |  | 
|  | struct HttpData; | 
|  | bool HttpShouldKeepAlive(const HttpData& data); | 
|  |  | 
|  | typedef std::pair<std::string, std::string> HttpAttribute; | 
|  | typedef std::vector<HttpAttribute> HttpAttributeList; | 
|  | void HttpComposeAttributes(const HttpAttributeList& attributes, char separator, | 
|  | std::string* composed); | 
|  | void HttpParseAttributes(const char * data, size_t len, | 
|  | HttpAttributeList& attributes); | 
|  | bool HttpHasAttribute(const HttpAttributeList& attributes, | 
|  | const std::string& name, | 
|  | std::string* value); | 
|  | bool HttpHasNthAttribute(HttpAttributeList& attributes, | 
|  | size_t index, | 
|  | std::string* name, | 
|  | std::string* value); | 
|  |  | 
|  | // Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp | 
|  | bool HttpDateToSeconds(const std::string& date, time_t* seconds); | 
|  |  | 
|  | inline uint16 HttpDefaultPort(bool secure) { | 
|  | return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT; | 
|  | } | 
|  |  | 
|  | // Returns the http server notation for a given address | 
|  | std::string HttpAddress(const SocketAddress& address, bool secure); | 
|  |  | 
|  | // functional for insensitive std::string compare | 
|  | struct iless { | 
|  | bool operator()(const std::string& lhs, const std::string& rhs) const { | 
|  | return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // put quotes around a string and escape any quotes inside it | 
|  | std::string quote(const std::string& str); | 
|  |  | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  | // Url | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | template<class CTYPE> | 
|  | class Url { | 
|  | public: | 
|  | typedef typename Traits<CTYPE>::string string; | 
|  |  | 
|  | // TODO: Implement Encode/Decode | 
|  | static int Encode(const CTYPE* source, CTYPE* destination, size_t len); | 
|  | static int Encode(const string& source, string& destination); | 
|  | static int Decode(const CTYPE* source, CTYPE* destination, size_t len); | 
|  | static int Decode(const string& source, string& destination); | 
|  |  | 
|  | Url(const string& url) { do_set_url(url.c_str(), url.size()); } | 
|  | Url(const string& path, const string& host, uint16 port = HTTP_DEFAULT_PORT) | 
|  | : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port) | 
|  | { set_full_path(path); } | 
|  |  | 
|  | bool valid() const { return !host_.empty(); } | 
|  | void clear() { | 
|  | host_.clear(); | 
|  | port_ = HTTP_DEFAULT_PORT; | 
|  | secure_ = false; | 
|  | path_.assign(1, static_cast<CTYPE>('/')); | 
|  | query_.clear(); | 
|  | } | 
|  |  | 
|  | void set_url(const string& val) { | 
|  | do_set_url(val.c_str(), val.size()); | 
|  | } | 
|  | string url() const { | 
|  | string val; do_get_url(&val); return val; | 
|  | } | 
|  |  | 
|  | void set_address(const string& val) { | 
|  | do_set_address(val.c_str(), val.size()); | 
|  | } | 
|  | string address() const { | 
|  | string val; do_get_address(&val); return val; | 
|  | } | 
|  |  | 
|  | void set_full_path(const string& val) { | 
|  | do_set_full_path(val.c_str(), val.size()); | 
|  | } | 
|  | string full_path() const { | 
|  | string val; do_get_full_path(&val); return val; | 
|  | } | 
|  |  | 
|  | void set_host(const string& val) { host_ = val; } | 
|  | const string& host() const { return host_; } | 
|  |  | 
|  | void set_port(uint16 val) { port_ = val; } | 
|  | uint16 port() const { return port_; } | 
|  |  | 
|  | void set_secure(bool val) { secure_ = val; } | 
|  | bool secure() const { return secure_; } | 
|  |  | 
|  | void set_path(const string& val) { | 
|  | if (val.empty()) { | 
|  | path_.assign(1, static_cast<CTYPE>('/')); | 
|  | } else { | 
|  | ASSERT(val[0] == static_cast<CTYPE>('/')); | 
|  | path_ = val; | 
|  | } | 
|  | } | 
|  | const string& path() const { return path_; } | 
|  |  | 
|  | void set_query(const string& val) { | 
|  | ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('?'))); | 
|  | query_ = val; | 
|  | } | 
|  | const string& query() const { return query_; } | 
|  |  | 
|  | bool get_attribute(const string& name, string* value) const; | 
|  |  | 
|  | private: | 
|  | void do_set_url(const CTYPE* val, size_t len); | 
|  | void do_set_address(const CTYPE* val, size_t len); | 
|  | void do_set_full_path(const CTYPE* val, size_t len); | 
|  |  | 
|  | void do_get_url(string* val) const; | 
|  | void do_get_address(string* val) const; | 
|  | void do_get_full_path(string* val) const; | 
|  |  | 
|  | string host_, path_, query_; | 
|  | uint16 port_; | 
|  | bool secure_; | 
|  | }; | 
|  |  | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  | // HttpData | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | struct HttpData { | 
|  | typedef std::multimap<std::string, std::string, iless> HeaderMap; | 
|  | typedef HeaderMap::const_iterator const_iterator; | 
|  | typedef HeaderMap::iterator iterator; | 
|  |  | 
|  | HttpVersion version; | 
|  | scoped_ptr<StreamInterface> document; | 
|  |  | 
|  | HttpData(); | 
|  |  | 
|  | enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW }; | 
|  | void changeHeader(const std::string& name, const std::string& value, | 
|  | HeaderCombine combine); | 
|  | inline void addHeader(const std::string& name, const std::string& value, | 
|  | bool append = true) { | 
|  | changeHeader(name, value, append ? HC_AUTO : HC_NO); | 
|  | } | 
|  | inline void setHeader(const std::string& name, const std::string& value, | 
|  | bool overwrite = true) { | 
|  | changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW); | 
|  | } | 
|  | // Returns count of erased headers | 
|  | size_t clearHeader(const std::string& name); | 
|  | // Returns iterator to next header | 
|  | iterator clearHeader(iterator header); | 
|  |  | 
|  | // keep in mind, this may not do what you want in the face of multiple headers | 
|  | bool hasHeader(const std::string& name, std::string* value) const; | 
|  |  | 
|  | inline const_iterator begin() const { | 
|  | return headers_.begin(); | 
|  | } | 
|  | inline const_iterator end() const { | 
|  | return headers_.end(); | 
|  | } | 
|  | inline iterator begin() { | 
|  | return headers_.begin(); | 
|  | } | 
|  | inline iterator end() { | 
|  | return headers_.end(); | 
|  | } | 
|  | inline const_iterator begin(const std::string& name) const { | 
|  | return headers_.lower_bound(name); | 
|  | } | 
|  | inline const_iterator end(const std::string& name) const { | 
|  | return headers_.upper_bound(name); | 
|  | } | 
|  | inline iterator begin(const std::string& name) { | 
|  | return headers_.lower_bound(name); | 
|  | } | 
|  | inline iterator end(const std::string& name) { | 
|  | return headers_.upper_bound(name); | 
|  | } | 
|  |  | 
|  | // Convenience methods using HttpHeader | 
|  | inline void changeHeader(HttpHeader header, const std::string& value, | 
|  | HeaderCombine combine) { | 
|  | changeHeader(ToString(header), value, combine); | 
|  | } | 
|  | inline void addHeader(HttpHeader header, const std::string& value, | 
|  | bool append = true) { | 
|  | addHeader(ToString(header), value, append); | 
|  | } | 
|  | inline void setHeader(HttpHeader header, const std::string& value, | 
|  | bool overwrite = true) { | 
|  | setHeader(ToString(header), value, overwrite); | 
|  | } | 
|  | inline void clearHeader(HttpHeader header) { | 
|  | clearHeader(ToString(header)); | 
|  | } | 
|  | inline bool hasHeader(HttpHeader header, std::string* value) const { | 
|  | return hasHeader(ToString(header), value); | 
|  | } | 
|  | inline const_iterator begin(HttpHeader header) const { | 
|  | return headers_.lower_bound(ToString(header)); | 
|  | } | 
|  | inline const_iterator end(HttpHeader header) const { | 
|  | return headers_.upper_bound(ToString(header)); | 
|  | } | 
|  | inline iterator begin(HttpHeader header) { | 
|  | return headers_.lower_bound(ToString(header)); | 
|  | } | 
|  | inline iterator end(HttpHeader header) { | 
|  | return headers_.upper_bound(ToString(header)); | 
|  | } | 
|  |  | 
|  | void setContent(const std::string& content_type, StreamInterface* document); | 
|  | void setDocumentAndLength(StreamInterface* document); | 
|  |  | 
|  | virtual size_t formatLeader(char* buffer, size_t size) const = 0; | 
|  | virtual HttpError parseLeader(const char* line, size_t len) = 0; | 
|  |  | 
|  | protected: | 
|  | virtual ~HttpData(); | 
|  | void clear(bool release_document); | 
|  | void copy(const HttpData& src); | 
|  |  | 
|  | private: | 
|  | HeaderMap headers_; | 
|  | }; | 
|  |  | 
|  | struct HttpRequestData : public HttpData { | 
|  | HttpVerb verb; | 
|  | std::string path; | 
|  |  | 
|  | HttpRequestData() : verb(HV_GET) { } | 
|  |  | 
|  | void clear(bool release_document); | 
|  | void copy(const HttpRequestData& src); | 
|  |  | 
|  | size_t formatLeader(char* buffer, size_t size) const override; | 
|  | HttpError parseLeader(const char* line, size_t len) override; | 
|  |  | 
|  | bool getAbsoluteUri(std::string* uri) const; | 
|  | bool getRelativeUri(std::string* host, std::string* path) const; | 
|  | }; | 
|  |  | 
|  | struct HttpResponseData : public HttpData { | 
|  | uint32 scode; | 
|  | std::string message; | 
|  |  | 
|  | HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { } | 
|  | void clear(bool release_document); | 
|  | void copy(const HttpResponseData& src); | 
|  |  | 
|  | // Convenience methods | 
|  | void set_success(uint32 scode = HC_OK); | 
|  | void set_success(const std::string& content_type, StreamInterface* document, | 
|  | uint32 scode = HC_OK); | 
|  | void set_redirect(const std::string& location, | 
|  | uint32 scode = HC_MOVED_TEMPORARILY); | 
|  | void set_error(uint32 scode); | 
|  |  | 
|  | size_t formatLeader(char* buffer, size_t size) const override; | 
|  | HttpError parseLeader(const char* line, size_t len) override; | 
|  | }; | 
|  |  | 
|  | struct HttpTransaction { | 
|  | HttpRequestData request; | 
|  | HttpResponseData response; | 
|  | }; | 
|  |  | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  | // Http Authentication | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | struct HttpAuthContext { | 
|  | std::string auth_method; | 
|  | HttpAuthContext(const std::string& auth) : auth_method(auth) { } | 
|  | virtual ~HttpAuthContext() { } | 
|  | }; | 
|  |  | 
|  | enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR }; | 
|  |  | 
|  | // 'context' is used by this function to record information between calls. | 
|  | // Start by passing a null pointer, then pass the same pointer each additional | 
|  | // call.  When the authentication attempt is finished, delete the context. | 
|  | HttpAuthResult HttpAuthenticate( | 
|  | const char * challenge, size_t len, | 
|  | const SocketAddress& server, | 
|  | const std::string& method, const std::string& uri, | 
|  | const std::string& username, const CryptString& password, | 
|  | HttpAuthContext *& context, std::string& response, std::string& auth_method); | 
|  |  | 
|  | ////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | } // namespace rtc | 
|  |  | 
|  | #endif // WEBRTC_BASE_HTTPCOMMON_H__ |