| /* |
| * libjingle |
| * Copyright 2004--2005, Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef TALK_BASE_HTTPCOMMON_H__ |
| #define TALK_BASE_HTTPCOMMON_H__ |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| #include "talk/base/basictypes.h" |
| #include "talk/base/common.h" |
| #include "talk/base/scoped_ptr.h" |
| #include "talk/base/stringutils.h" |
| #include "talk/base/stream.h" |
| |
| namespace talk_base { |
| |
| 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, unsigned long* 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() : version(HVER_1_1) { } |
| |
| 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); |
| |
| virtual size_t formatLeader(char* buffer, size_t size) const; |
| virtual HttpError parseLeader(const char* line, size_t len); |
| |
| 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); |
| |
| virtual size_t formatLeader(char* buffer, size_t size) const; |
| virtual HttpError parseLeader(const char* line, size_t len); |
| }; |
| |
| 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 talk_base |
| |
| #endif // TALK_BASE_HTTPCOMMON_H__ |