aboutsummaryrefslogtreecommitdiffstats
path: root/websocketpp/http
diff options
context:
space:
mode:
authorLuca Muscariello <lumuscar+fdio@cisco.com>2017-02-25 23:42:31 +0100
committerLuca Muscariello <lumuscar+fdio@cisco.com>2017-02-25 23:42:31 +0100
commit05c1a838c881ea502888659848d8792843b28718 (patch)
treecf0b05b58bd725a1eb6c80325ba986c63dea42aa /websocketpp/http
parent9b30fc10fb1cbebe651e5a107e8ca5b24de54675 (diff)
Initial commit: video player - viper
Change-Id: Id5aa33598ce34659bad4a7a9ae5006bfb84f9bd1 Signed-off-by: Luca Muscariello <lumuscar+fdio@cisco.com>
Diffstat (limited to 'websocketpp/http')
-rw-r--r--websocketpp/http/constants.hpp308
-rw-r--r--websocketpp/http/impl/parser.hpp196
-rw-r--r--websocketpp/http/impl/request.hpp191
-rw-r--r--websocketpp/http/impl/response.hpp266
-rw-r--r--websocketpp/http/parser.hpp619
-rw-r--r--websocketpp/http/request.hpp124
-rw-r--r--websocketpp/http/response.hpp188
7 files changed, 1892 insertions, 0 deletions
diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp
new file mode 100644
index 00000000..f946cb31
--- /dev/null
+++ b/websocketpp/http/constants.hpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PETER THORSON 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 HTTP_CONSTANTS_HPP
+#define HTTP_CONSTANTS_HPP
+
+#include <exception>
+#include <map>
+#include <string>
+#include <vector>
+#include <utility>
+
+namespace websocketpp {
+/// HTTP handling support
+namespace http {
+ /// 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<std::string,std::string> attribute_list;
+
+ /// 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<std::string,attribute_list> > parameter_list;
+
+ /// Literal value of the HTTP header delimiter
+ static char const header_delimiter[] = "\r\n";
+
+ /// 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;
+
+ /// Default Maximum size in bytes for HTTP message bodies.
+ size_t const max_body_size = 32000000;
+
+ /// 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
+ 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 30..3f
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 40..4f
+ 1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, // 50..5f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 60..6f
+ 1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0, // 70..7f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 80..8f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 90..9f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // a0..af
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // b0..bf
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0..cf
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0..df
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0..ef
+ 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];
+ }
+
+ /// 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,
+
+ continue_code = 100,
+ switching_protocols = 101,
+
+ ok = 200,
+ created = 201,
+ accepted = 202,
+ non_authoritative_information = 203,
+ no_content = 204,
+ reset_content = 205,
+ partial_content = 206,
+
+ multiple_choices = 300,
+ moved_permanently = 301,
+ found = 302,
+ see_other = 303,
+ not_modified = 304,
+ use_proxy = 305,
+ temporary_redirect = 307,
+
+ bad_request = 400,
+ unauthorized = 401,
+ payment_required = 402,
+ forbidden = 403,
+ not_found = 404,
+ method_not_allowed = 405,
+ not_acceptable = 406,
+ proxy_authentication_required = 407,
+ request_timeout = 408,
+ conflict = 409,
+ gone = 410,
+ length_required = 411,
+ precondition_failed = 412,
+ request_entity_too_large = 413,
+ request_uri_too_long = 414,
+ unsupported_media_type = 415,
+ request_range_not_satisfiable = 416,
+ expectation_failed = 417,
+ im_a_teapot = 418,
+ upgrade_required = 426,
+ precondition_required = 428,
+ too_many_requests = 429,
+ request_header_fields_too_large = 431,
+
+ internal_server_error = 500,
+ not_implemented = 501,
+ bad_gateway = 502,
+ service_unavailable = 503,
+ gateway_timeout = 504,
+ http_version_not_supported = 505,
+ not_extended = 510,
+ network_authentication_required = 511
+ };
+
+ // TODO: should this be inline?
+ inline std::string get_string(value c) {
+ switch (c) {
+ case uninitialized:
+ return "Uninitialized";
+ case continue_code:
+ return "Continue";
+ case switching_protocols:
+ return "Switching Protocols";
+ case ok:
+ return "OK";
+ case created:
+ return "Created";
+ case accepted:
+ return "Accepted";
+ case non_authoritative_information:
+ return "Non Authoritative Information";
+ case no_content:
+ return "No Content";
+ case reset_content:
+ return "Reset Content";
+ case partial_content:
+ return "Partial Content";
+ case multiple_choices:
+ return "Multiple Choices";
+ case moved_permanently:
+ return "Moved Permanently";
+ case found:
+ return "Found";
+ case see_other:
+ return "See Other";
+ case not_modified:
+ return "Not Modified";
+ case use_proxy:
+ return "Use Proxy";
+ case temporary_redirect:
+ return "Temporary Redirect";
+ case bad_request:
+ return "Bad Request";
+ case unauthorized:
+ return "Unauthorized";
+ case payment_required:
+ return "Payment Required";
+ case forbidden:
+ return "Forbidden";
+ case not_found:
+ return "Not Found";
+ case method_not_allowed:
+ return "Method Not Allowed";
+ case not_acceptable:
+ return "Not Acceptable";
+ case proxy_authentication_required:
+ return "Proxy Authentication Required";
+ case request_timeout:
+ return "Request Timeout";
+ case conflict:
+ return "Conflict";
+ case gone:
+ return "Gone";
+ case length_required:
+ return "Length Required";
+ case precondition_failed:
+ return "Precondition Failed";
+ case request_entity_too_large:
+ return "Request Entity Too Large";
+ case request_uri_too_long:
+ return "Request-URI Too Long";
+ case unsupported_media_type:
+ return "Unsupported Media Type";
+ case request_range_not_satisfiable:
+ return "Requested Range Not Satisfiable";
+ case expectation_failed:
+ return "Expectation Failed";
+ case im_a_teapot:
+ return "I'm a teapot";
+ case upgrade_required:
+ return "Upgrade Required";
+ case precondition_required:
+ return "Precondition Required";
+ case too_many_requests:
+ return "Too Many Requests";
+ case request_header_fields_too_large:
+ return "Request Header Fields Too Large";
+ case internal_server_error:
+ return "Internal Server Error";
+ case not_implemented:
+ return "Not Implemented";
+ case bad_gateway:
+ return "Bad Gateway";
+ case service_unavailable:
+ return "Service Unavailable";
+ case gateway_timeout:
+ return "Gateway Timeout";
+ case http_version_not_supported:
+ return "HTTP Version Not Supported";
+ case not_extended:
+ return "Not Extended";
+ case network_authentication_required:
+ return "Network Authentication Required";
+ default:
+ return "Unknown";
+ }
+ }
+ }
+
+ class exception : public std::exception {
+ public:
+ exception(const std::string& log_msg,
+ status_code::value error_code,
+ const std::string& error_msg = std::string(),
+ const std::string& body = std::string())
+ : m_msg(log_msg)
+ , m_error_msg(error_msg)
+ , m_body(body)
+ , m_error_code(error_code) {}
+
+ ~exception() throw() {}
+
+ virtual const char* what() const throw() {
+ return m_msg.c_str();
+ }
+
+ std::string m_msg;
+ std::string m_error_msg;
+ std::string m_body;
+ status_code::value m_error_code;
+ };
+}
+}
+
+#endif // HTTP_CONSTANTS_HPP
diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp
new file mode 100644
index 00000000..1d59b938
--- /dev/null
+++ b/websocketpp/http/impl/parser.hpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PETER THORSON 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 HTTP_PARSER_IMPL_HPP
+#define HTTP_PARSER_IMPL_HPP
+
+#include <algorithm>
+#include <cstdlib>
+#include <istream>
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+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;
+ }
+}
+
+inline bool parser::get_header_as_plist(std::string const & key,
+ parameter_list & out) const
+{
+ header_list::const_iterator it = m_headers.find(key);
+
+ if (it == m_headers.end() || it->second.size() == 0) {
+ return false;
+ }
+
+ return this->parse_parameter_list(it->second,out);
+}
+
+inline void parser::append_header(std::string const & key, std::string const &
+ val)
+{
+ if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) {
+ throw exception("Invalid header name",status_code::bad_request);
+ }
+
+ if (this->get_header(key).empty()) {
+ m_headers[key] = val;
+ } else {
+ m_headers[key] += ", " + val;
+ }
+}
+
+inline void parser::replace_header(std::string const & key, std::string const &
+ val)
+{
+ m_headers[key] = val;
+}
+
+inline void parser::remove_header(std::string const & key) {
+ m_headers.erase(key);
+}
+
+inline void parser::set_body(std::string const & value) {
+ if (value.size() == 0) {
+ remove_header("Content-Length");
+ m_body.clear();
+ return;
+ }
+
+ // TODO: should this method respect the max size? If so how should errors
+ // be indicated?
+
+ std::stringstream len;
+ len << value.size();
+ replace_header("Content-Length", len.str());
+ m_body = value;
+}
+
+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::prepare_body() {
+ if (!get_header("Content-Length").empty()) {
+ std::string const & cl_header = get_header("Content-Length");
+ char * end;
+
+ // TODO: not 100% sure what the compatibility of this method is. Also,
+ // I believe this will only work up to 32bit sizes. Is there a need for
+ // > 4GiB HTTP payloads?
+ m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10);
+
+ if (m_body_bytes_needed > m_body_bytes_max) {
+ throw exception("HTTP message body too large",
+ status_code::request_entity_too_large);
+ }
+
+ m_body_encoding = body_encoding::plain;
+ return true;
+ } else if (get_header("Transfer-Encoding") == "chunked") {
+ // TODO
+ //m_body_encoding = body_encoding::chunked;
+ return false;
+ } else {
+ return false;
+ }
+}
+
+inline size_t parser::process_body(char const * buf, size_t len) {
+ if (m_body_encoding == body_encoding::plain) {
+ size_t processed = (std::min)(m_body_bytes_needed,len);
+ m_body.append(buf,processed);
+ m_body_bytes_needed -= processed;
+ return processed;
+ } else if (m_body_encoding == body_encoding::chunked) {
+ // TODO:
+ throw exception("Unexpected body encoding",
+ status_code::internal_server_error);
+ } else {
+ throw exception("Unexpected body encoding",
+ status_code::internal_server_error);
+ }
+}
+
+inline void parser::process_header(std::string::iterator begin,
+ std::string::iterator end)
+{
+ std::string::iterator cursor = std::search(
+ begin,
+ end,
+ header_separator,
+ header_separator + sizeof(header_separator) - 1
+ );
+
+ if (cursor == end) {
+ throw exception("Invalid header line",status_code::bad_request);
+ }
+
+ append_header(strip_lws(std::string(begin,cursor)),
+ strip_lws(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
+
+#endif // HTTP_PARSER_IMPL_HPP
diff --git a/websocketpp/http/impl/request.hpp b/websocketpp/http/impl/request.hpp
new file mode 100644
index 00000000..311a620f
--- /dev/null
+++ b/websocketpp/http/impl/request.hpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PETER THORSON 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 HTTP_PARSER_REQUEST_IMPL_HPP
+#define HTTP_PARSER_REQUEST_IMPL_HPP
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+inline size_t request::consume(char const * buf, size_t len) {
+ size_t bytes_processed;
+
+ if (m_ready) {return 0;}
+
+ if (m_body_bytes_needed > 0) {
+ bytes_processed = process_body(buf,len);
+ if (body_ready()) {
+ m_ready = true;
+ }
+ return bytes_processed;
+ }
+
+ // copy new header bytes into buffer
+ m_buf->append(buf,len);
+
+ // Search for delimiter in buf. If found read until then. If not read all
+ std::string::iterator begin = m_buf->begin();
+ std::string::iterator end;
+
+ for (;;) {
+ // search for line delimiter
+ end = std::search(
+ begin,
+ m_buf->end(),
+ header_delimiter,
+ header_delimiter+sizeof(header_delimiter)-1
+ );
+
+ m_header_bytes += (end-begin+sizeof(header_delimiter));
+
+ if (m_header_bytes > max_header_size) {
+ // exceeded max header size
+ throw exception("Maximum header size exceeded.",
+ status_code::request_header_fields_too_large);
+ }
+
+ if (end == m_buf->end()) {
+ // we are out of bytes. Discard the processed bytes and copy the
+ // remaining unprecessed bytes to the beginning of the buffer
+ std::copy(begin,end,m_buf->begin());
+ m_buf->resize(static_cast<std::string::size_type>(end-begin));
+ m_header_bytes -= m_buf->size();
+
+ return len;
+ }
+
+ //the range [begin,end) now represents a line to be processed.
+ if (end-begin == 0) {
+ // we got a blank line
+ if (m_method.empty() || get_header("Host").empty()) {
+ throw exception("Incomplete Request",status_code::bad_request);
+ }
+
+ bytes_processed = (
+ len - static_cast<std::string::size_type>(m_buf->end()-end)
+ + sizeof(header_delimiter) - 1
+ );
+
+ // frees memory used temporarily during request parsing
+ m_buf.reset();
+
+ // if this was not an upgrade request and has a content length
+ // continue capturing content-length bytes and expose them as a
+ // request body.
+
+ if (prepare_body()) {
+ bytes_processed += process_body(buf+bytes_processed,len-bytes_processed);
+ if (body_ready()) {
+ m_ready = true;
+ }
+ return bytes_processed;
+ } else {
+ m_ready = true;
+
+ // return number of bytes processed (starting bytes - bytes left)
+ return bytes_processed;
+ }
+ } else {
+ if (m_method.empty()) {
+ this->process(begin,end);
+ } else {
+ this->process_header(begin,end);
+ }
+ }
+
+ begin = end+(sizeof(header_delimiter)-1);
+ }
+}
+
+inline std::string request::raw() const {
+ // TODO: validation. Make sure all required fields have been set?
+ std::stringstream ret;
+
+ ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
+ ret << raw_headers() << "\r\n" << m_body;
+
+ return ret.str();
+}
+
+inline std::string request::raw_head() const {
+ // TODO: validation. Make sure all required fields have been set?
+ std::stringstream ret;
+
+ ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
+ ret << raw_headers() << "\r\n";
+
+ return ret.str();
+}
+
+inline void request::set_method(std::string const & method) {
+ if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) {
+ throw exception("Invalid method token.",status_code::bad_request);
+ }
+
+ m_method = method;
+}
+
+inline void request::set_uri(std::string const & uri) {
+ // TODO: validation?
+ m_uri = uri;
+}
+
+inline void request::process(std::string::iterator begin, std::string::iterator
+ end)
+{
+ std::string::iterator cursor_start = begin;
+ std::string::iterator cursor_end = std::find(begin,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid request line1",status_code::bad_request);
+ }
+
+ set_method(std::string(cursor_start,cursor_end));
+
+ cursor_start = cursor_end+1;
+ cursor_end = std::find(cursor_start,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid request line2",status_code::bad_request);
+ }
+
+ set_uri(std::string(cursor_start,cursor_end));
+ set_version(std::string(cursor_end+1,end));
+}
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#endif // HTTP_PARSER_REQUEST_IMPL_HPP
diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp
new file mode 100644
index 00000000..4400cda5
--- /dev/null
+++ b/websocketpp/http/impl/response.hpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PETER THORSON 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 HTTP_PARSER_RESPONSE_IMPL_HPP
+#define HTTP_PARSER_RESPONSE_IMPL_HPP
+
+#include <algorithm>
+#include <istream>
+#include <sstream>
+#include <string>
+
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+inline size_t response::consume(char const * buf, size_t len) {
+ if (m_state == DONE) {return 0;}
+
+ if (m_state == BODY) {
+ return this->process_body(buf,len);
+ }
+
+ // copy new header bytes into buffer
+ m_buf->append(buf,len);
+
+ // Search for delimiter in buf. If found read until then. If not read all
+ std::string::iterator begin = m_buf->begin();
+ std::string::iterator end = begin;
+
+
+ for (;;) {
+ // search for delimiter
+ end = std::search(
+ begin,
+ m_buf->end(),
+ header_delimiter,
+ header_delimiter + sizeof(header_delimiter) - 1
+ );
+
+ m_header_bytes += (end-begin+sizeof(header_delimiter));
+
+ if (m_header_bytes > max_header_size) {
+ // exceeded max header size
+ throw exception("Maximum header size exceeded.",
+ status_code::request_header_fields_too_large);
+ }
+
+ if (end == m_buf->end()) {
+ // we are out of bytes. Discard the processed bytes and copy the
+ // remaining unprecessed bytes to the beginning of the buffer
+ std::copy(begin,end,m_buf->begin());
+ m_buf->resize(static_cast<std::string::size_type>(end-begin));
+
+ m_read += len;
+ m_header_bytes -= m_buf->size();
+
+ return len;
+ }
+
+ //the range [begin,end) now represents a line to be processed.
+
+ if (end-begin == 0) {
+ // we got a blank line
+ if (m_state == RESPONSE_LINE) {
+ throw exception("Incomplete Request",status_code::bad_request);
+ }
+
+ // TODO: grab content-length
+ std::string length = get_header("Content-Length");
+
+ if (length.empty()) {
+ // no content length found, read indefinitely
+ m_read = 0;
+ } else {
+ std::istringstream ss(length);
+
+ if ((ss >> m_read).fail()) {
+ throw exception("Unable to parse Content-Length header",
+ status_code::bad_request);
+ }
+ }
+
+ m_state = BODY;
+
+ // calc header bytes processed (starting bytes - bytes left)
+ size_t read = (
+ len - static_cast<std::string::size_type>(m_buf->end() - end)
+ + sizeof(header_delimiter) - 1
+ );
+
+ // if there were bytes left process them as body bytes
+ if (read < len) {
+ read += this->process_body(buf+read,(len-read));
+ }
+
+ // frees memory used temporarily during header parsing
+ m_buf.reset();
+
+ return read;
+ } else {
+ if (m_state == RESPONSE_LINE) {
+ this->process(begin,end);
+ m_state = HEADERS;
+ } else {
+ this->process_header(begin,end);
+ }
+ }
+
+ begin = end+(sizeof(header_delimiter) - 1);
+ }
+}
+
+inline size_t response::consume(std::istream & s) {
+ char buf[istream_buffer];
+ size_t bytes_read;
+ size_t bytes_processed;
+ size_t total = 0;
+
+ while (s.good()) {
+ s.getline(buf,istream_buffer);
+ bytes_read = static_cast<size_t>(s.gcount());
+
+ if (s.fail() || s.eof()) {
+ bytes_processed = this->consume(buf,bytes_read);
+ total += bytes_processed;
+
+ if (bytes_processed != bytes_read) {
+ // problem
+ break;
+ }
+ } else if (s.bad()) {
+ // problem
+ break;
+ } else {
+ // the delimiting newline was found. Replace the trailing null with
+ // the newline that was discarded, since our raw consume function
+ // expects the newline to be be there.
+ buf[bytes_read-1] = '\n';
+ bytes_processed = this->consume(buf,bytes_read);
+ total += bytes_processed;
+
+ if (bytes_processed != bytes_read) {
+ // problem
+ break;
+ }
+ }
+ }
+
+ return total;
+}
+
+inline std::string response::raw() const {
+ // TODO: validation. Make sure all required fields have been set?
+
+ std::stringstream ret;
+
+ ret << get_version() << " " << m_status_code << " " << m_status_msg;
+ ret << "\r\n" << raw_headers() << "\r\n";
+
+ ret << m_body;
+
+ return ret.str();
+}
+
+inline void response::set_status(status_code::value code) {
+ // TODO: validation?
+ m_status_code = code;
+ m_status_msg = get_string(code);
+}
+
+inline void response::set_status(status_code::value code, std::string const &
+ msg)
+{
+ // TODO: validation?
+ m_status_code = code;
+ m_status_msg = msg;
+}
+
+inline void response::process(std::string::iterator begin,
+ std::string::iterator end)
+{
+ std::string::iterator cursor_start = begin;
+ std::string::iterator cursor_end = std::find(begin,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid response line",status_code::bad_request);
+ }
+
+ set_version(std::string(cursor_start,cursor_end));
+
+ cursor_start = cursor_end+1;
+ cursor_end = std::find(cursor_start,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid request line",status_code::bad_request);
+ }
+
+ int code;
+
+ std::istringstream ss(std::string(cursor_start,cursor_end));
+
+ if ((ss >> code).fail()) {
+ throw exception("Unable to parse response code",status_code::bad_request);
+ }
+
+ set_status(status_code::value(code),std::string(cursor_end+1,end));
+}
+
+inline size_t response::process_body(char const * buf, size_t len) {
+ // If no content length was set then we read forever and never set m_ready
+ if (m_read == 0) {
+ //m_body.append(buf,len);
+ //return len;
+ m_state = DONE;
+ return 0;
+ }
+
+ // Otherwise m_read is the number of bytes left.
+ size_t to_read;
+
+ if (len >= m_read) {
+ // if we have more bytes than we need read, read only the amount needed
+ // then set done state
+ to_read = m_read;
+ m_state = DONE;
+ } else {
+ // we need more bytes than are available, read them all
+ to_read = len;
+ }
+
+ m_body.append(buf,to_read);
+ m_read -= to_read;
+ return to_read;
+}
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#endif // HTTP_PARSER_RESPONSE_IMPL_HPP
diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp
new file mode 100644
index 00000000..90f49ebe
--- /dev/null
+++ b/websocketpp/http/parser.hpp
@@ -0,0 +1,619 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PETER THORSON 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 HTTP_PARSER_HPP
+#define HTTP_PARSER_HPP
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <utility>
+
+#include <websocketpp/utilities.hpp>
+#include <websocketpp/http/constants.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+namespace state {
+ enum value {
+ method,
+ resource,
+ version,
+ headers
+ };
+}
+
+namespace body_encoding {
+ enum value {
+ unknown,
+ plain,
+ chunked
+ };
+}
+
+typedef std::map<std::string, std::string, utility::ci_less > header_list;
+
+/// 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 <typename InputIterator>
+std::pair<std::string,InputIterator> extract_token(InputIterator begin,
+ InputIterator end)
+{
+ InputIterator it = std::find_if(begin,end,&is_not_token_char);
+ 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 <typename InputIterator>
+std::pair<std::string,InputIterator> extract_quoted_string(InputIterator begin,
+ InputIterator end)
+{
+ std::string s;
+
+ if (end == begin) {
+ return std::make_pair(s,begin);
+ }
+
+ if (*begin != '"') {
+ return std::make_pair(s,begin);
+ }
+
+ InputIterator cursor = begin+1;
+ InputIterator marker = cursor;
+
+ cursor = std::find(cursor,end,'"');
+
+ while (cursor != end) {
+ // either this is the end or a quoted string
+ if (*(cursor-1) == '\\') {
+ s.append(marker,cursor-1);
+ s.append(1,'"');
+ ++cursor;
+ marker = cursor;
+ } else {
+ s.append(marker,cursor);
+ ++cursor;
+ return std::make_pair(s,cursor);
+ }
+
+ cursor = std::find(cursor,end,'"');
+ }
+
+ return std::make_pair("",begin);
+}
+
+/// 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 <typename InputIterator>
+InputIterator extract_lws(InputIterator begin, InputIterator end) {
+ InputIterator it = begin;
+
+ // strip leading CRLF
+ if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' &&
+ is_whitespace_char(static_cast<unsigned char>(*(begin+2))))
+ {
+ it+=3;
+ }
+
+ it = std::find_if(it,end,&is_not_whitespace_char);
+ return it;
+}
+
+/// 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 <typename InputIterator>
+InputIterator extract_all_lws(InputIterator begin, InputIterator end) {
+ InputIterator old_it;
+ InputIterator new_it = begin;
+
+ do {
+ // Pull value from previous iteration
+ old_it = new_it;
+
+ // look ahead another pass
+ new_it = extract_lws(old_it,end);
+ } while (new_it != end && old_it != new_it);
+
+ return new_it;
+}
+
+/// 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 <typename InputIterator>
+InputIterator extract_attributes(InputIterator begin, InputIterator end,
+ attribute_list & attributes)
+{
+ InputIterator cursor;
+ bool first = true;
+
+ if (begin == end) {
+ return begin;
+ }
+
+ cursor = begin;
+ std::pair<std::string,InputIterator> ret;
+
+ while (cursor != end) {
+ std::string name;
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {
+ break;
+ }
+
+ if (first) {
+ // ignore this check for the very first pass
+ first = false;
+ } else {
+ if (*cursor == ';') {
+ // advance past the ';'
+ ++cursor;
+ } else {
+ // non-semicolon in this position indicates end end of the
+ // attribute list, break and return.
+ break;
+ }
+ }
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ ret = http::parser::extract_token(cursor,end);
+
+ if (ret.first.empty()) {
+ // error: expected a token
+ return begin;
+ } else {
+ name = ret.first;
+ cursor = ret.second;
+ }
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end || *cursor != '=') {
+ // if there is an equals sign, read the attribute value. Otherwise
+ // record a blank value and continue
+ attributes[name].clear();
+ continue;
+ }
+
+ // advance past the '='
+ ++cursor;
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {
+ // error: expected a token or quoted string
+ return begin;
+ }
+
+ ret = http::parser::extract_quoted_string(cursor,end);
+ if (ret.second != cursor) {
+ attributes[name] = ret.first;
+ cursor = ret.second;
+ continue;
+ }
+
+ ret = http::parser::extract_token(cursor,end);
+ if (ret.first.empty()) {
+ // error : expected token or quoted string
+ return begin;
+ } else {
+ attributes[name] = ret.first;
+ cursor = ret.second;
+ }
+ }
+
+ 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 <typename InputIterator>
+InputIterator extract_parameters(InputIterator begin, InputIterator end,
+ parameter_list &parameters)
+{
+ InputIterator cursor;
+
+ if (begin == end) {
+ // error: expected non-zero length range
+ return begin;
+ }
+
+ cursor = begin;
+ std::pair<std::string,InputIterator> ret;
+
+ /**
+ * LWS
+ * token
+ * LWS
+ * *(";" method-param)
+ * LWS
+ * ,=loop again
+ */
+ while (cursor != end) {
+ std::string parameter_name;
+ attribute_list attributes;
+
+ // extract any stray whitespace
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {break;}
+
+ ret = http::parser::extract_token(cursor,end);
+
+ if (ret.first.empty()) {
+ // error: expected a token
+ return begin;
+ } else {
+ parameter_name = ret.first;
+ cursor = ret.second;
+ }
+
+ // Safe break point, insert parameter with blank attributes and exit
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {
+ //parameters[parameter_name] = attributes;
+ parameters.push_back(std::make_pair(parameter_name,attributes));
+ break;
+ }
+
+ // If there is an attribute list, read it in
+ if (*cursor == ';') {
+ InputIterator acursor;
+
+ ++cursor;
+ acursor = http::parser::extract_attributes(cursor,end,attributes);
+
+ if (acursor == cursor) {
+ // attribute extraction ended in syntax error
+ return begin;
+ }
+
+ cursor = acursor;
+ }
+
+ // insert parameter into output list
+ //parameters[parameter_name] = attributes;
+ parameters.push_back(std::make_pair(parameter_name,attributes));
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {break;}
+
+ // if next char is ',' then read another parameter, else stop
+ if (*cursor != ',') {
+ break;
+ }
+
+ // advance past comma
+ ++cursor;
+
+ if (cursor == end) {
+ // expected more bytes after a comma
+ return begin;
+ }
+ }
+
+ return cursor;
+}
+
+inline std::string strip_lws(std::string const & input) {
+ std::string::const_iterator begin = extract_all_lws(input.begin(),input.end());
+ if (begin == input.end()) {
+ return std::string();
+ }
+
+ std::string::const_reverse_iterator rbegin = extract_all_lws(input.rbegin(),input.rend());
+ if (rbegin == input.rend()) {
+ return std::string();
+ }
+
+ return std::string(begin,rbegin.base());
+}
+
+/// Base HTTP parser
+/**
+ * Includes methods and data elements common to all types of HTTP messages such
+ * as headers, versions, bodies, etc.
+ */
+class parser {
+public:
+ parser()
+ : m_header_bytes(0)
+ , m_body_bytes_needed(0)
+ , m_body_bytes_max(max_body_size)
+ , m_body_encoding(body_encoding::unknown) {}
+
+ /// Get the HTTP version string
+ /**
+ * @return The version string for this parser
+ */
+ std::string const & get_version() const {
+ return m_version;
+ }
+
+ /// Set HTTP parser Version
+ /**
+ * 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.
+ */
+ 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);
+
+ /// Get HTTP body
+ /**
+ * Gets the body of the HTTP object
+ *
+ * @return The body of the HTTP message.
+ */
+ std::string const & get_body() const {
+ return m_body;
+ }
+
+ /// Set body content
+ /**
+ * Set the body content of the HTTP response to the parameter string. Note
+ * set_body will also set the Content-Length HTTP header to the appropriate
+ * value. If you want the Content-Length header to be something else, do so
+ * via replace_header("Content-Length") after calling set_body()
+ *
+ * @param value String data to include as the body content.
+ */
+ void set_body(std::string const & value);
+
+ /// Get body size limit
+ /**
+ * Retrieves the maximum number of bytes to parse & buffer before canceling
+ * a request.
+ *
+ * @since 0.5.0
+ *
+ * @return The maximum length of a message body.
+ */
+ size_t get_max_body_size() const {
+ return m_body_bytes_max;
+ }
+
+ /// Set body size limit
+ /**
+ * Set the maximum number of bytes to parse and buffer before canceling a
+ * request.
+ *
+ * @since 0.5.0
+ *
+ * @param value The size to set the max body length to.
+ */
+ void set_max_body_size(size_t value) {
+ m_body_bytes_max = 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:
+ /// 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);
+
+ /// Prepare the parser to begin parsing body data
+ /**
+ * Inspects headers to determine if the message has a body that needs to be
+ * read. If so, sets up the necessary state, otherwise returns false. If
+ * this method returns true and loading the message body is desired call
+ * `process_body` until it returns zero bytes or an error.
+ *
+ * Must not be called until after all headers have been processed.
+ *
+ * @since 0.5.0
+ *
+ * @return True if more bytes are needed to load the body, false otherwise.
+ */
+ bool prepare_body();
+
+ /// Process body data
+ /**
+ * Parses body data.
+ *
+ * @since 0.5.0
+ *
+ * @param [in] begin An iterator to the beginning of the sequence.
+ * @param [in] end An iterator to the end of the sequence.
+ * @return The number of bytes processed
+ */
+ size_t process_body(char const * buf, size_t len);
+
+ /// Check if the parser is done parsing the body
+ /**
+ * Behavior before a call to `prepare_body` is undefined.
+ *
+ * @since 0.5.0
+ *
+ * @return True if the message body has been completed loaded.
+ */
+ bool body_ready() const {
+ return (m_body_bytes_needed == 0);
+ }
+
+ /// 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;
+ header_list m_headers;
+
+ size_t m_header_bytes;
+
+ std::string m_body;
+ size_t m_body_bytes_needed;
+ size_t m_body_bytes_max;
+ body_encoding::value m_body_encoding;
+};
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#include <websocketpp/http/impl/parser.hpp>
+
+#endif // HTTP_PARSER_HPP
diff --git a/websocketpp/http/request.hpp b/websocketpp/http/request.hpp
new file mode 100644
index 00000000..3355c99b
--- /dev/null
+++ b/websocketpp/http/request.hpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PETER THORSON 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 HTTP_PARSER_REQUEST_HPP
+#define HTTP_PARSER_REQUEST_HPP
+
+#include <string>
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+/// Stores, parses, and manipulates HTTP requests
+/**
+ * http::request provides the following functionality for working with HTTP
+ * requests.
+ *
+ * - Initialize request via manually setting each element
+ * - Initialize request via reading raw bytes and parsing
+ * - Once initialized, access individual parsed elements
+ * - Once initialized, read entire request as raw bytes
+ */
+class request : public parser {
+public:
+ typedef request type;
+ typedef lib::shared_ptr<type> ptr;
+
+ request()
+ : m_buf(lib::make_shared<std::string>())
+ , m_ready(false) {}
+
+ /// Process bytes in the input buffer
+ /**
+ * Process up to len bytes from input buffer buf. Returns the number of
+ * bytes processed. Bytes left unprocessed means bytes left over after the
+ * final header delimiters.
+ *
+ * Consume is a streaming processor. It may be called multiple times on one
+ * request and the full headers need not be available before processing can
+ * begin. If the end of the request was reached during this call to consume
+ * the ready flag will be set. Further calls to consume once ready will be
+ * ignored.
+ *
+ * Consume will throw an http::exception in the case of an error. Typical
+ * error reasons include malformed requests, incomplete requests, and max
+ * header size being reached.
+ *
+ * @param buf Pointer to byte buffer
+ * @param len Size of byte buffer
+ * @return Number of bytes processed.
+ */
+ size_t consume(char const * buf, size_t len);
+
+ /// Returns whether or not the request is ready for reading.
+ bool ready() const {
+ return m_ready;
+ }
+
+ /// Returns the full raw request (including the body)
+ std::string raw() const;
+
+ /// Returns the raw request headers only (similar to an HTTP HEAD request)
+ std::string raw_head() const;
+
+ /// Set the HTTP method. Must be a valid HTTP token
+ void set_method(std::string const & method);
+
+ /// Return the request method
+ std::string const & get_method() const {
+ return m_method;
+ }
+
+ /// Set the HTTP uri. Must be a valid HTTP uri
+ void set_uri(std::string const & uri);
+
+ /// Return the requested URI
+ std::string const & get_uri() const {
+ return m_uri;
+ }
+
+private:
+ /// Helper function for message::consume. Process request line
+ void process(std::string::iterator begin, std::string::iterator end);
+
+ lib::shared_ptr<std::string> m_buf;
+ std::string m_method;
+ std::string m_uri;
+ bool m_ready;
+};
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#include <websocketpp/http/impl/request.hpp>
+
+#endif // HTTP_PARSER_REQUEST_HPP
diff --git a/websocketpp/http/response.hpp b/websocketpp/http/response.hpp
new file mode 100644
index 00000000..e724a3d3
--- /dev/null
+++ b/websocketpp/http/response.hpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PETER THORSON 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 HTTP_PARSER_RESPONSE_HPP
+#define HTTP_PARSER_RESPONSE_HPP
+
+#include <iostream>
+#include <string>
+
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+/// Stores, parses, and manipulates HTTP responses
+/**
+ * http::response provides the following functionality for working with HTTP
+ * responses.
+ *
+ * - Initialize response via manually setting each element
+ * - Initialize response via reading raw bytes and parsing
+ * - Once initialized, access individual parsed elements
+ * - Once initialized, read entire response as raw bytes
+ *
+ * http::response checks for header completeness separately from the full
+ * response. Once the header is complete, the Content-Length header is read to
+ * determine when to stop reading body bytes. If no Content-Length is present
+ * ready() will never return true. It is the responsibility of the caller to
+ * consume to determine when the response is complete (ie when the connection
+ * terminates, or some other metric).
+ */
+class response : public parser {
+public:
+ typedef response type;
+ typedef lib::shared_ptr<type> ptr;
+
+ response()
+ : m_read(0)
+ , m_buf(lib::make_shared<std::string>())
+ , m_status_code(status_code::uninitialized)
+ , m_state(RESPONSE_LINE) {}
+
+ /// Process bytes in the input buffer
+ /**
+ * Process up to len bytes from input buffer buf. Returns the number of
+ * bytes processed. Bytes left unprocessed means bytes left over after the
+ * final header delimiters.
+ *
+ * Consume is a streaming processor. It may be called multiple times on one
+ * response and the full headers need not be available before processing can
+ * begin. If the end of the response was reached during this call to consume
+ * the ready flag will be set. Further calls to consume once ready will be
+ * ignored.
+ *
+ * Consume will throw an http::exception in the case of an error. Typical
+ * error reasons include malformed responses, incomplete responses, and max
+ * header size being reached.
+ *
+ * @param buf Pointer to byte buffer
+ * @param len Size of byte buffer
+ * @return Number of bytes processed.
+ */
+ size_t consume(char const * buf, size_t len);
+
+ /// Process bytes in the input buffer (istream version)
+ /**
+ * Process bytes from istream s. Returns the number of bytes processed.
+ * Bytes left unprocessed means bytes left over after the final header
+ * delimiters.
+ *
+ * Consume is a streaming processor. It may be called multiple times on one
+ * response and the full headers need not be available before processing can
+ * begin. If the end of the response was reached during this call to consume
+ * the ready flag will be set. Further calls to consume once ready will be
+ * ignored.
+ *
+ * Consume will throw an http::exception in the case of an error. Typical
+ * error reasons include malformed responses, incomplete responses, and max
+ * header size being reached.
+ *
+ * @param buf Pointer to byte buffer
+ * @param len Size of byte buffer
+ * @return Number of bytes processed.
+ */
+ size_t consume(std::istream & s);
+
+ /// Returns true if the response is ready.
+ /**
+ * @note will never return true if the content length header is not present
+ */
+ bool ready() const {
+ return m_state == DONE;
+ }
+
+ /// Returns true if the response headers are fully parsed.
+ bool headers_ready() const {
+ return (m_state == BODY || m_state == DONE);
+ }
+
+ /// Returns the full raw response
+ std::string raw() const;
+
+ /// Set response status code and message
+ /**
+ * Sets the response status code to `code` and looks up the corresponding
+ * message for standard codes. Non-standard codes will be entered as Unknown
+ * use set_status(status_code::value,std::string) overload to set both
+ * values explicitly.
+ *
+ * @param code Code to set
+ * @param msg Message to set
+ */
+ void set_status(status_code::value code);
+
+ /// Set response status code and message
+ /**
+ * Sets the response status code and message to independent custom values.
+ * use set_status(status_code::value) to set the code and have the standard
+ * message be automatically set.
+ *
+ * @param code Code to set
+ * @param msg Message to set
+ */
+ void set_status(status_code::value code, std::string const & msg);
+
+ /// Return the response status code
+ status_code::value get_status_code() const {
+ return m_status_code;
+ }
+
+ /// Return the response status message
+ const std::string& get_status_msg() const {
+ return m_status_msg;
+ }
+private:
+ /// Helper function for consume. Process response line
+ void process(std::string::iterator begin, std::string::iterator end);
+
+ /// Helper function for processing body bytes
+ size_t process_body(char const * buf, size_t len);
+
+ enum state {
+ RESPONSE_LINE = 0,
+ HEADERS = 1,
+ BODY = 2,
+ DONE = 3
+ };
+
+ std::string m_status_msg;
+ size_t m_read;
+ lib::shared_ptr<std::string> m_buf;
+ status_code::value m_status_code;
+ state m_state;
+
+};
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#include <websocketpp/http/impl/response.hpp>
+
+#endif // HTTP_PARSER_RESPONSE_HPP