From 743cd7a713d8cdb43e311fb881baebd236f157d4 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Tue, 25 Jun 2013 20:57:30 -0500 Subject: [PATCH] HTTP cleanup and documentation --- test/http/parser.cpp | 8 +- test/processors/hybi13.cpp | 2 +- websocketpp/http/constants.hpp | 55 ++++-- websocketpp/http/impl/parser.hpp | 169 ++++------------- websocketpp/http/parser.hpp | 303 ++++++++++++++++++++----------- websocketpp/http/request.hpp | 3 - 6 files changed, 281 insertions(+), 259 deletions(-) diff --git a/test/http/parser.cpp b/test/http/parser.cpp index 49604268fe..c982d2ca26 100644 --- a/test/http/parser.cpp +++ b/test/http/parser.cpp @@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE( extract_all_lws ) { BOOST_AUTO_TEST_CASE( extract_attributes_blank ) { std::string s = ""; - websocketpp::http::parser::attribute_list a; + websocketpp::http::attribute_list a; std::string::const_iterator it; it = websocketpp::http::parser::extract_attributes(s.begin(),s.end(),a); @@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE( extract_attributes_blank ) { BOOST_AUTO_TEST_CASE( extract_attributes_simple ) { std::string s = "foo"; - websocketpp::http::parser::attribute_list a; + websocketpp::http::attribute_list a; std::string::const_iterator it; it = websocketpp::http::parser::extract_attributes(s.begin(),s.end(),a); @@ -224,8 +224,8 @@ BOOST_AUTO_TEST_CASE( extract_parameters ) { std::string sx = "foo;bar=\"a \\\"b\\\" c\""; - websocketpp::http::parser::parameter_list p; - websocketpp::http::parser::attribute_list a; + websocketpp::http::parameter_list p; + websocketpp::http::attribute_list a; std::string::const_iterator it; using websocketpp::http::parser::extract_parameters; diff --git a/test/processors/hybi13.cpp b/test/processors/hybi13.cpp index 20470f930e..602985ad17 100644 --- a/test/processors/hybi13.cpp +++ b/test/processors/hybi13.cpp @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp index 5df6ddc0f6..f117048730 100644 --- a/websocketpp/http/constants.hpp +++ b/websocketpp/http/constants.hpp @@ -28,24 +28,48 @@ #ifndef HTTP_CONSTANTS_HPP #define HTTP_CONSTANTS_HPP +#include #include +#include namespace websocketpp { namespace http { - static const char header_delimiter[] = "\r\n"; - static const char header_separator[] = ": "; - static const std::string empty_header = ""; + /// The type of an HTTP attribute list + /** + * The attribute list is an unordered key/value map. Encoded attribute + * values are delimited by semicolons. + */ + typedef std::map attribute_list; - // Maximum size in bytes before rejecting an HTTP header as too big. - const size_t max_header_size = 16000; + /// The type of an HTTP parameter list + /** + * The parameter list is an ordered pairing of a parameter and its + * associated attribute list. Encoded parameter values are delimited by + * commas. + */ + typedef std::vector< std::pair > parameter_list; - // Number of bytes to use for temporary istream read buffers - const size_t istream_buffer = 512; + /// Literal value of the HTTP header delimiter + static char const header_delimiter[] = "\r\n"; - // invalid HTTP token characters - // 0x00 - 0x32, 0x7f-0xff - // ( ) < > @ , ; : \ " / [ ] ? = { } - static const char header_token[] = { + /// Literal value of the HTTP header separator + static char const header_separator[] = ": "; + + /// Literal value of an empty header + static std::string const empty_header = ""; + + /// Maximum size in bytes before rejecting an HTTP header as too big. + size_t const max_header_size = 16000; + + /// Number of bytes to use for temporary istream read buffers + size_t const istream_buffer = 512; + + /// invalid HTTP token characters + /** + * 0x00 - 0x32, 0x7f-0xff + * ( ) < > @ , ; : \ " / [ ] ? = { } + */ + static char const header_token[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..0f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 10..1f 0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0, // 20..2f @@ -64,23 +88,30 @@ namespace http { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // f0..ff }; + /// Is the character a token inline bool is_token_char(unsigned char c) { return (header_token[c] == 1); } + /// Is the character a non-token inline bool is_not_token_char(unsigned char c) { return !header_token[c]; } - // Space (32) or horizontal tab (9) + /// Is the character whitespace + /** + * whitespace is space (32) or horizontal tab (9) + */ inline bool is_whitespace_char(unsigned char c) { return (c == 9 || c == 32); } + /// Is the character non-whitespace inline bool is_not_whitespace_char(unsigned char c) { return (c != 9 && c != 32); } + /// HTTP Status codes namespace status_code { enum value { uninitialized = 0, diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index c34ae83176..d46d6ace6d 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -35,37 +35,20 @@ namespace websocketpp { namespace http { namespace parser { -/// Extract an HTTP parameter list from a string. -/** - * @param [in] in The input string. - * - * @param [out] out The parameter list to store extracted parameters in. - * - * @return Whether or not the input was a valid parameter list. - */ -inline bool parser::parse_parameter_list(std::string const & in, - parameter_list & out) const -{ - if (in.size() == 0) { - return false; - } - - std::string::const_iterator it; - it = extract_parameters(in.begin(),in.end(),out); - return (it == in.begin()); +inline void parser::set_version(std::string const & version) { + m_version = version; +} + +inline std::string const & parser::get_header(std::string const & key) const { + header_list::const_iterator h = m_headers.find(key); + + if (h == m_headers.end()) { + return empty_header; + } else { + return h->second; + } } -/// Extract an HTTP parameter list from a parser header. -/** - * If the header requested doesn't exist or exists and is empty the parameter - * list is valid (but empty). - * - * @param [in] key The name/key of the HTTP header to use as input. - * - * @param [out] out The parameter list to store extracted parameters in. - * - * @return Whether or not the input was a valid parameter list. - */ inline bool parser::get_header_as_plist(std::string const & key, parameter_list & out) const { @@ -78,48 +61,6 @@ inline bool parser::get_header_as_plist(std::string const & key, return this->parse_parameter_list(it->second,out); } -/// Set HTTP parser Version -/** - * \todo Does this method need any validation? - * - * @param [in] version The value to set the HTTP version to. - */ -inline void parser::set_version(std::string const & version) { - m_version = version; -} - -/// Get the value of an HTTP header -/** - * \todo Make this method case insensitive. - * - * @param [in] key The name/key of the header to get. - * - * @return The value associated with the given HTTP header key. - */ -inline std::string const & parser::get_header(std::string const & key) const { - header_list::const_iterator h = m_headers.find(key); - - if (h == m_headers.end()) { - return empty_header; - } else { - return h->second; - } -} - -/// Append a value to an existing HTTP header -/** - * This method will set the value of the HTTP header `key` with the indicated - * value. If a header with the name `key` already exists, `val` will be appended - * to the existing value. - * - * \todo Make this method case insensitive. - * \todo Should there be any restrictions on which keys are allowed to be set? - * \todo Exception free varient - * - * @param [in] key The name/key of the header to append to. - * - * @param [in] val The value to append. - */ inline void parser::append_header(std::string const & key, std::string const & val) { @@ -134,46 +75,16 @@ inline void parser::append_header(std::string const & key, std::string const & } } -/// Set a value for an HTTP header, replacing an existing value -/** - * This method will set the value of the HTTP header `key` with the indicated - * value. If a header with the name `key` already exists, `val` will replace the - * existing value. - * - * \todo Make this method case insensitive. - * \todo Should there be any restrictions on which keys are allowed to be set? - * \todo Exception free varient - * - * @param [in] key The name/key of the header to append to. - * - * @param [in] val The value to append. - */ inline void parser::replace_header(std::string const & key, std::string const & val) { m_headers[key] = val; } -/// Remove a header from the parser -/** - * Removes the header entirely from the parser. This is different than setting - * the value of the header to blank. - * - * \todo Make this method case insensitive. - * - * @param [in] key The name/key of the header to remove. - */ inline void parser::remove_header(std::string const & key) { m_headers.erase(key); } -/// Set HTTP body -/** - * Sets the body of the HTTP object and fills in the appropriate content length - * header - * - * @param [in] value The value to set the body to. - */ inline void parser::set_body(std::string const & value) { if (value.size() == 0) { remove_header("Content-Length"); @@ -187,11 +98,19 @@ inline void parser::set_body(std::string const & value) { m_body = value; } -/// Parse headers from an istream -/** - * @param [in] s The istream to extract headers from. - */ -inline bool parser::parse_headers(std::istream& s) { +inline bool parser::parse_parameter_list(std::string const & in, + parameter_list & out) const +{ + if (in.size() == 0) { + return false; + } + + std::string::const_iterator it; + it = extract_parameters(in.begin(),in.end(),out); + return (it == in.begin()); +} + +inline bool parser::parse_headers(std::istream & s) { std::string header; std::string::size_type end; @@ -213,31 +132,6 @@ inline bool parser::parse_headers(std::istream& s) { return true; } -/// Generate and return the HTTP headers as a string -/** - * Each headers will be followed by the \r\n sequence including the last one. - * A second \r\n sequence (blank header) is not appended by this method - * - * @return The HTTP headers as a string. - */ -inline std::string parser::raw_headers() const { - std::stringstream raw; - - header_list::const_iterator it; - for (it = m_headers.begin(); it != m_headers.end(); it++) { - raw << it->first << ": " << it->second << "\r\n"; - } - - return raw.str(); -} - -/// Process a header -/** - * - * \todo Update this method to be exception free. - * - * @param [in] s The istream to extract headers from. - */ inline void parser::process_header(std::string::iterator begin, std::string::iterator end) { @@ -256,6 +150,19 @@ inline void parser::process_header(std::string::iterator begin, std::string(cursor+sizeof(header_separator)-1,end)); } +inline std::string parser::raw_headers() const { + std::stringstream raw; + + header_list::const_iterator it; + for (it = m_headers.begin(); it != m_headers.end(); it++) { + raw << it->first << ": " << it->second << "\r\n"; + } + + return raw.str(); +} + + + } // namespace parser } // namespace http } // namespace websocketpp diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index 845bbb218b..34dea64793 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -49,8 +49,16 @@ namespace state { typedef std::map header_list; -/// Read until a non-token character is found and then return the token and -/// iterator to the next character to read +/// Read and return the next token in the stream +/** + * Read until a non-token character is found and then return the token and + * iterator to the next character to read + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return A pair containing the token and an iterator to the next character in + * the stream + */ template std::pair extract_token(InputIterator begin, InputIterator end) @@ -59,6 +67,17 @@ std::pair extract_token(InputIterator begin, return std::make_pair(std::string(begin,it),it); } +/// Read and return the next quoted string in the stream +/** + * Read a double quoted string starting at `begin`. The quotes themselves are + * stripped. The quoted value is returned along with an iterator to the next + * character to read + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return A pair containing the string read and an iterator to the next + * character in the stream + */ template std::pair extract_quoted_string(InputIterator begin, InputIterator end) @@ -97,8 +116,15 @@ std::pair extract_quoted_string(InputIterator begin, return std::make_pair("",begin); } -/// Read one unit of linear white space and return the iterator to the character -/// afterwards. If ret = begin no whitespace was extracted. +/// Read and discard one unit of linear whitespace +/** + * Read one unit of linear white space and return the iterator to the character + * afterwards. If `begin` is returned, no whitespace was extracted. + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return An iterator to the character after the linear whitespace read + */ template InputIterator extract_lws(InputIterator begin, InputIterator end) { InputIterator it = begin; @@ -114,7 +140,16 @@ InputIterator extract_lws(InputIterator begin, InputIterator end) { return it; } -/// SImilar to extract_lws but extracts all lws instead of just one line +/// Read and discard linear whitespace +/** + * Read linear white space until a non-lws character is read and return an + * iterator to that character. If `begin` is returned, no whitespace was + * extracted. + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return An iterator to the character after the linear whitespace read + */ template InputIterator extract_all_lws(InputIterator begin, InputIterator end) { InputIterator old_it; @@ -131,40 +166,21 @@ InputIterator extract_all_lws(InputIterator begin, InputIterator end) { return new_it; } -/* -struct attribute { - attribute(const std::string &n, const std::string &v) - : name(n), value(v){} - - std::string name; - std::string value; -}; -typedef std::vector attribute_list; - -struct parameter { - parameter(std::string n) : name(n) {} - - void add_attribute(const attribute& p) { - attributes.push_back(p); - } - - void add_attribute(const std::string& key, const std::string & value) { - attributes.push_back(attribute(key,value)); - } - - std::string name; - attribute_list attributes; -}; -typedef std::vector parameter_list; -*/ - -//typedef std::map string_map; -//typedef std::vector< std::pair< std::string, attribute_list > > parameter_list; - -typedef std::map attribute_list; -//typedef std::map parameter_list; -typedef std::vector< std::pair< std::string, attribute_list > > parameter_list; - +/// Extract HTTP attributes +/** + * An http attributes list is a semicolon delimited list of key value pairs in + * the format: *( ";" attribute "=" value ) where attribute is a token and value + * is a token or quoted string. + * + * Attributes extracted are appended to the supplied attributes list + * `attributes`. + * + * @param [in] begin An iterator to the beginning of the sequence + * @param [in] end An iterator to the end of the sequence + * @param [out] attributes A reference to the attributes list to append + * attribute/value pairs extracted to + * @return An iterator to the character after the last atribute read + */ template InputIterator extract_attributes(InputIterator begin, InputIterator end, attribute_list & attributes) @@ -249,9 +265,23 @@ InputIterator extract_attributes(InputIterator begin, InputIterator end, return cursor; } +/// Extract HTTP parameters +/** + * An http parameters list is a comma delimited list of tokens followed by + * optional semicolon delimited attributes lists. + * + * Parameters extracted are appended to the supplied parameters list + * `parameters`. + * + * @param [in] begin An iterator to the beginning of the sequence + * @param [in] end An iterator to the end of the sequence + * @param [out] parameters A reference to the parameters list to append + * paramter values extracted to + * @return An iterator to the character after the last parameter read + */ template InputIterator extract_parameters(InputIterator begin, InputIterator end, - parameter_list ¶meters) + parameter_list ¶meters) { InputIterator cursor; @@ -336,76 +366,107 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end, return cursor; } +/// Base HTTP parser +/** + * Includes methods and data elements common to all types of HTTP messages such + * as headers, versions, bodies, etc. + */ class parser { -public: - typedef http::parser::attribute_list attribute_list; - typedef http::parser::parameter_list parameter_list; - - // Convenience method versions of some of the free utility functions. - bool parse_parameter_list(const std::string& in, parameter_list& out) const; - - /// Set the HTTP version string - /** - * @param version HTTP version string to use. Must be in format HTTP/x.y - * where x and y are positive integers. - */ - void set_version(const std::string& version); - +public: /// Get the HTTP version string - const std::string& get_version() const { + /** + * @return The version string for this parser + */ + std::string const & get_version() const { return m_version; } - - /// Get the HTTP header with name `key` + + /// Set HTTP parser Version /** - * @param key Name of the header to return - * @return Value of the header + * Input should be in format: HTTP/x.y where x and y are positive integers. + * @todo Does this method need any validation? + * + * @param [in] version The value to set the HTTP version to. */ - const std::string& get_header(const std::string& key) const; - - /// Get the body string - const std::string& get_body() const { + void set_version(std::string const & version); + + /// Get the value of an HTTP header + /** + * @todo Make this method case insensitive. + * + * @param [in] key The name/key of the header to get. + * @return The value associated with the given HTTP header key. + */ + std::string const & get_header(std::string const & key) const; + + /// Extract an HTTP parameter list from a parser header. + /** + * If the header requested doesn't exist or exists and is empty the + * parameter list is valid (but empty). + * + * @param [in] key The name/key of the HTTP header to use as input. + * @param [out] out The parameter list to store extracted parameters in. + * @return Whether or not the input was a valid parameter list. + */ + bool get_header_as_plist(std::string const & key, parameter_list & out) + const; + + /// Append a value to an existing HTTP header + /** + * This method will set the value of the HTTP header `key` with the + * indicated value. If a header with the name `key` already exists, `val` + * will be appended to the existing value. + * + * @todo Make this method case insensitive. + * @todo Should there be any restrictions on which keys are allowed? + * @todo Exception free varient + * + * @see replace_header + * + * @param [in] key The name/key of the header to append to. + * @param [in] val The value to append. + */ + void append_header(std::string const & key, std::string const & val); + + /// Set a value for an HTTP header, replacing an existing value + /** + * This method will set the value of the HTTP header `key` with the + * indicated value. If a header with the name `key` already exists, `val` + * will replace the existing value. + * + * @todo Make this method case insensitive. + * @todo Should there be any restrictions on which keys are allowed? + * @todo Exception free varient + * + * @see append_header + * + * @param [in] key The name/key of the header to append to. + * @param [in] val The value to append. + */ + void replace_header(std::string const & key, std::string const & val); + + /// Remove a header from the parser + /** + * Removes the header entirely from the parser. This is different than + * setting the value of the header to blank. + * + * @todo Make this method case insensitive. + * + * @param [in] key The name/key of the header to remove. + */ + void remove_header(std::string const & key); + + /// Set HTTP body + /** + * Sets the body of the HTTP object and fills in the appropriate content + * length header. + * + * @param [in] value The value to set the body to. + */ + std::string const & get_body() const { return m_body; } - - /// Get the HTTP header with name `key` - /** - * - * @param key The header name to retrieve - * - * @param out A reference to a parameter list to store any extracted - * paramters. - * - * @return True if the value of this header was not a valid parameter list - */ - bool get_header_as_plist(const std::string& key, parameter_list& out) const; - /// Append a header - /** - * If a header with this name already exists the value will be appended to - * the existing header to form a comma separated list of values. Use - * replace_header to overwrite existing values. - * - * @param key Name of the header to set - * @param val Value to add - * @see replace_header - */ - void append_header(const std::string &key,const std::string &val); - - /// Replace a header - /** - * If a header with this name already exists the old value will be replaced - * Use add_header to append to a list of existing values. - * - * @param key Name of the header to set - * @param val Value to set - * @see add_header - */ - void replace_header(const std::string &key,const std::string &val); - - /// Remove a header - void remove_header(const std::string &key); - /// Set body content /** * Set the body content of the HTTP response to the parameter string. Note @@ -415,15 +476,41 @@ public: * * @param value String data to include as the body content. */ - void set_body(const std::string& value); + void set_body(std::string const & value); + + /// Extract an HTTP parameter list from a string. + /** + * @param [in] in The input string. + * @param [out] out The parameter list to store extracted parameters in. + * @return Whether or not the input was a valid parameter list. + */ + bool parse_parameter_list(std::string const & in, parameter_list & out) + const; protected: - /// DEPRECATED Read headers out of an istream - bool parse_headers(std::istream& s); - - /// Helper function for consume. Process header line + /// Parse headers from an istream + /** + * @deprecated Use process_header instead. + * + * @param [in] s The istream to extract headers from. + */ + bool parse_headers(std::istream & s); + + /// Process a header line + /** + * @todo Update this method to be exception free. + * + * @param [in] begin An iterator to the beginning of the sequence. + * @param [in] end An iterator to the end of the sequence. + */ void process_header(std::string::iterator begin, std::string::iterator end); - - /// Return headers in raw string form. + + /// Generate and return the HTTP headers as a string + /** + * Each headers will be followed by the \r\n sequence including the last one. + * A second \r\n sequence (blank header) is not appended by this method + * + * @return The HTTP headers as a string. + */ std::string raw_headers() const; std::string m_version; diff --git a/websocketpp/http/request.hpp b/websocketpp/http/request.hpp index 87d90feb94..09edff69b6 100644 --- a/websocketpp/http/request.hpp +++ b/websocketpp/http/request.hpp @@ -49,9 +49,6 @@ class request : public parser { public: typedef request type; typedef lib::shared_ptr ptr; - - typedef parser::attribute_list attribute_list; - typedef parser::parameter_list parameter_list; request() : m_buf(new std::string())