aboutsummaryrefslogtreecommitdiffstats
path: root/websocketpp/processors
diff options
context:
space:
mode:
Diffstat (limited to 'websocketpp/processors')
-rw-r--r--websocketpp/processors/base.hpp299
-rw-r--r--websocketpp/processors/hybi00.hpp462
-rw-r--r--websocketpp/processors/hybi07.hpp78
-rw-r--r--websocketpp/processors/hybi08.hpp83
-rw-r--r--websocketpp/processors/hybi13.hpp1056
-rw-r--r--websocketpp/processors/processor.hpp407
6 files changed, 2385 insertions, 0 deletions
diff --git a/websocketpp/processors/base.hpp b/websocketpp/processors/base.hpp
new file mode 100644
index 00000000..ddb8b81a
--- /dev/null
+++ b/websocketpp/processors/base.hpp
@@ -0,0 +1,299 @@
+/*
+ * 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 WEBSOCKETPP_PROCESSOR_BASE_HPP
+#define WEBSOCKETPP_PROCESSOR_BASE_HPP
+
+#include <websocketpp/close.hpp>
+#include <websocketpp/utilities.hpp>
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace processor {
+
+/// Constants related to processing WebSocket connections
+namespace constants {
+
+static char const upgrade_token[] = "websocket";
+static char const connection_token[] = "upgrade";
+static char const handshake_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+} // namespace constants
+
+
+/// Processor class related error codes
+namespace error_cat {
+enum value {
+ BAD_REQUEST = 0, // Error was the result of improperly formatted user input
+ INTERNAL_ERROR = 1, // Error was a logic error internal to WebSocket++
+ PROTOCOL_VIOLATION = 2,
+ MESSAGE_TOO_BIG = 3,
+ PAYLOAD_VIOLATION = 4 // Error was due to receiving invalid payload data
+};
+} // namespace error_cat
+
+/// Error code category and codes used by all processor types
+namespace error {
+enum processor_errors {
+ /// Catch-all error for processor policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// Error was the result of improperly formatted user input
+ bad_request,
+
+ /// Processor encountered a protocol violation in an incoming message
+ protocol_violation,
+
+ /// Processor encountered a message that was too large
+ message_too_big,
+
+ /// Processor encountered invalid payload data.
+ invalid_payload,
+
+ /// The processor method was called with invalid arguments
+ invalid_arguments,
+
+ /// Opcode was invalid for requested operation
+ invalid_opcode,
+
+ /// Control frame too large
+ control_too_big,
+
+ /// Illegal use of reserved bit
+ invalid_rsv_bit,
+
+ /// Fragmented control message
+ fragmented_control,
+
+ /// Continuation without message
+ invalid_continuation,
+
+ /// Clients may not send unmasked frames
+ masking_required,
+
+ /// Servers may not send masked frames
+ masking_forbidden,
+
+ /// Payload length not minimally encoded
+ non_minimal_encoding,
+
+ /// Not supported on 32 bit systems
+ requires_64bit,
+
+ /// Invalid UTF-8 encoding
+ invalid_utf8,
+
+ /// Operation required not implemented functionality
+ not_implemented,
+
+ /// Invalid HTTP method
+ invalid_http_method,
+
+ /// Invalid HTTP version
+ invalid_http_version,
+
+ /// Invalid HTTP status
+ invalid_http_status,
+
+ /// Missing Required Header
+ missing_required_header,
+
+ /// Embedded SHA-1 library error
+ sha1_library,
+
+ /// No support for this feature in this protocol version.
+ no_protocol_support,
+
+ /// Reserved close code used
+ reserved_close_code,
+
+ /// Invalid close code used
+ invalid_close_code,
+
+ /// Using a reason requires a close code
+ reason_requires_code,
+
+ /// Error parsing subprotocols
+ subprotocol_parse_error,
+
+ /// Error parsing extensions
+ extension_parse_error,
+
+ /// Extension related operation was ignored because extensions are disabled
+ extensions_disabled,
+
+ /// Short Ke3 read. Hybi00 requires a third key to be read from the 8 bytes
+ /// after the handshake. Less than 8 bytes were read.
+ short_key3
+};
+
+/// Category for processor errors
+class processor_category : public lib::error_category {
+public:
+ processor_category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.processor";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case error::general:
+ return "Generic processor error";
+ case error::bad_request:
+ return "invalid user input";
+ case error::protocol_violation:
+ return "Generic protocol violation";
+ case error::message_too_big:
+ return "A message was too large";
+ case error::invalid_payload:
+ return "A payload contained invalid data";
+ case error::invalid_arguments:
+ return "invalid function arguments";
+ case error::invalid_opcode:
+ return "invalid opcode";
+ case error::control_too_big:
+ return "Control messages are limited to fewer than 125 characters";
+ case error::invalid_rsv_bit:
+ return "Invalid use of reserved bits";
+ case error::fragmented_control:
+ return "Control messages cannot be fragmented";
+ case error::invalid_continuation:
+ return "Invalid message continuation";
+ case error::masking_required:
+ return "Clients may not send unmasked frames";
+ case error::masking_forbidden:
+ return "Servers may not send masked frames";
+ case error::non_minimal_encoding:
+ return "Payload length was not minimally encoded";
+ case error::requires_64bit:
+ return "64 bit frames are not supported on 32 bit systems";
+ case error::invalid_utf8:
+ return "Invalid UTF8 encoding";
+ case error::not_implemented:
+ return "Operation required not implemented functionality";
+ case error::invalid_http_method:
+ return "Invalid HTTP method.";
+ case error::invalid_http_version:
+ return "Invalid HTTP version.";
+ case error::invalid_http_status:
+ return "Invalid HTTP status.";
+ case error::missing_required_header:
+ return "A required HTTP header is missing";
+ case error::sha1_library:
+ return "SHA-1 library error";
+ case error::no_protocol_support:
+ return "The WebSocket protocol version in use does not support this feature";
+ case error::reserved_close_code:
+ return "Reserved close code used";
+ case error::invalid_close_code:
+ return "Invalid close code used";
+ case error::reason_requires_code:
+ return "Using a close reason requires a valid close code";
+ case error::subprotocol_parse_error:
+ return "Error parsing subprotocol header";
+ case error::extension_parse_error:
+ return "Error parsing extension header";
+ case error::extensions_disabled:
+ return "Extensions are disabled";
+ case error::short_key3:
+ return "Short Hybi00 Key 3 read";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the processor error category
+inline lib::error_category const & get_processor_category() {
+ static processor_category instance;
+ return instance;
+}
+
+/// Create an error code with the given value and the processor category
+inline lib::error_code make_error_code(error::processor_errors e) {
+ return lib::error_code(static_cast<int>(e), get_processor_category());
+}
+
+/// Converts a processor error_code into a websocket close code
+/**
+ * Looks up the appropriate WebSocket close code that should be sent after an
+ * error of this sort occurred.
+ *
+ * If the error is not in the processor category close::status::blank is
+ * returned.
+ *
+ * If the error isn't normally associated with reasons to close a connection
+ * (such as errors intended to be used internally or delivered to client
+ * applications, ex: invalid arguments) then
+ * close::status::internal_endpoint_error is returned.
+ */
+inline close::status::value to_ws(lib::error_code ec) {
+ if (ec.category() != get_processor_category()) {
+ return close::status::blank;
+ }
+
+ switch (ec.value()) {
+ case error::protocol_violation:
+ case error::control_too_big:
+ case error::invalid_opcode:
+ case error::invalid_rsv_bit:
+ case error::fragmented_control:
+ case error::invalid_continuation:
+ case error::masking_required:
+ case error::masking_forbidden:
+ case error::reserved_close_code:
+ case error::invalid_close_code:
+ return close::status::protocol_error;
+ case error::invalid_payload:
+ case error::invalid_utf8:
+ return close::status::invalid_payload;
+ case error::message_too_big:
+ return close::status::message_too_big;
+ default:
+ return close::status::internal_endpoint_error;
+ }
+}
+
+} // namespace error
+} // namespace processor
+} // namespace websocketpp
+
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::processor::error::processor_errors>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif //WEBSOCKETPP_PROCESSOR_BASE_HPP
diff --git a/websocketpp/processors/hybi00.hpp b/websocketpp/processors/hybi00.hpp
new file mode 100644
index 00000000..95ad9dfa
--- /dev/null
+++ b/websocketpp/processors/hybi00.hpp
@@ -0,0 +1,462 @@
+/*
+ * 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 WEBSOCKETPP_PROCESSOR_HYBI00_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP
+
+#include <websocketpp/frame.hpp>
+#include <websocketpp/http/constants.hpp>
+
+#include <websocketpp/utf8_validator.hpp>
+#include <websocketpp/common/network.hpp>
+#include <websocketpp/common/md5.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <websocketpp/processors/processor.hpp>
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi Draft version 00
+/**
+ * There are many differences between Hybi 00 and Hybi 13
+ */
+template <typename config>
+class hybi00 : public processor<config> {
+public:
+ typedef processor<config> base;
+
+ typedef typename config::request_type request_type;
+ typedef typename config::response_type response_type;
+
+ typedef typename config::message_type message_type;
+ typedef typename message_type::ptr message_ptr;
+
+ typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
+
+ explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager)
+ : processor<config>(secure, p_is_server)
+ , msg_hdr(0x00)
+ , msg_ftr(0xff)
+ , m_state(HEADER)
+ , m_msg_manager(manager) {}
+
+ int get_version() const {
+ return 0;
+ }
+
+ lib::error_code validate_handshake(request_type const & r) const {
+ if (r.get_method() != "GET") {
+ return make_error_code(error::invalid_http_method);
+ }
+
+ if (r.get_version() != "HTTP/1.1") {
+ return make_error_code(error::invalid_http_version);
+ }
+
+ // required headers
+ // Host is required by HTTP/1.1
+ // Connection is required by is_websocket_handshake
+ // Upgrade is required by is_websocket_handshake
+ if (r.get_header("Sec-WebSocket-Key1").empty() ||
+ r.get_header("Sec-WebSocket-Key2").empty() ||
+ r.get_header("Sec-WebSocket-Key3").empty())
+ {
+ return make_error_code(error::missing_required_header);
+ }
+
+ return lib::error_code();
+ }
+
+ lib::error_code process_handshake(request_type const & req,
+ std::string const & subprotocol, response_type & res) const
+ {
+ char key_final[16];
+
+ // copy key1 into final key
+ decode_client_key(req.get_header("Sec-WebSocket-Key1"), &key_final[0]);
+
+ // copy key2 into final key
+ decode_client_key(req.get_header("Sec-WebSocket-Key2"), &key_final[4]);
+
+ // copy key3 into final key
+ // key3 should be exactly 8 bytes. If it is more it will be truncated
+ // if it is less the final key will almost certainly be wrong.
+ // TODO: decide if it is best to silently fail here or produce some sort
+ // of warning or exception.
+ std::string const & key3 = req.get_header("Sec-WebSocket-Key3");
+ std::copy(key3.c_str(),
+ key3.c_str()+(std::min)(static_cast<size_t>(8), key3.size()),
+ &key_final[8]);
+
+ res.append_header(
+ "Sec-WebSocket-Key3",
+ md5::md5_hash_string(std::string(key_final,16))
+ );
+
+ res.append_header("Upgrade","WebSocket");
+ res.append_header("Connection","Upgrade");
+
+ // Echo back client's origin unless our local application set a
+ // more restrictive one.
+ if (res.get_header("Sec-WebSocket-Origin").empty()) {
+ res.append_header("Sec-WebSocket-Origin",req.get_header("Origin"));
+ }
+
+ // Echo back the client's request host unless our local application
+ // set a different one.
+ if (res.get_header("Sec-WebSocket-Location").empty()) {
+ uri_ptr uri = get_uri(req);
+ res.append_header("Sec-WebSocket-Location",uri->str());
+ }
+
+ if (!subprotocol.empty()) {
+ res.replace_header("Sec-WebSocket-Protocol",subprotocol);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * The Hybi 00 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type &, uri_ptr,
+ std::vector<std::string> const &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ /// Validate the server's response to an outgoing handshake request
+ /**
+ * The Hybi 00 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param req The original request sent
+ * @param res The reponse to generate
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ lib::error_code validate_server_handshake_response(request_type const &,
+ response_type &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ std::string get_raw(response_type const & res) const {
+ response_type temp = res;
+ temp.remove_header("Sec-WebSocket-Key3");
+ return temp.raw() + res.get_header("Sec-WebSocket-Key3");
+ }
+
+ std::string const & get_origin(request_type const & r) const {
+ return r.get_header("Origin");
+ }
+
+ /// Extracts requested subprotocols from a handshake request
+ /**
+ * hybi00 does support subprotocols
+ * https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-1.9
+ *
+ * @param [in] req The request to extract from
+ * @param [out] subprotocol_list A reference to a vector of strings to store
+ * the results in.
+ */
+ lib::error_code extract_subprotocols(request_type const & req,
+ std::vector<std::string> & subprotocol_list)
+ {
+ if (!req.get_header("Sec-WebSocket-Protocol").empty()) {
+ http::parameter_list p;
+
+ if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) {
+ http::parameter_list::const_iterator it;
+
+ for (it = p.begin(); it != p.end(); ++it) {
+ subprotocol_list.push_back(it->first);
+ }
+ } else {
+ return error::make_error_code(error::subprotocol_parse_error);
+ }
+ }
+ return lib::error_code();
+ }
+
+ uri_ptr get_uri(request_type const & request) const {
+ std::string h = request.get_header("Host");
+
+ size_t last_colon = h.rfind(":");
+ size_t last_sbrace = h.rfind("]");
+
+ // no : = hostname with no port
+ // last : before ] = ipv6 literal with no port
+ // : with no ] = hostname with port
+ // : after ] = ipv6 literal with port
+
+ if (last_colon == std::string::npos ||
+ (last_sbrace != std::string::npos && last_sbrace > last_colon))
+ {
+ return lib::make_shared<uri>(base::m_secure, h, request.get_uri());
+ } else {
+ return lib::make_shared<uri>(base::m_secure,
+ h.substr(0,last_colon),
+ h.substr(last_colon+1),
+ request.get_uri());
+ }
+
+ // TODO: check if get_uri is a full uri
+ }
+
+ /// Get hybi00 handshake key3
+ /**
+ * @todo This doesn't appear to be used anymore. It might be able to be
+ * removed
+ */
+ std::string get_key3() const {
+ return "";
+ }
+
+ /// Process new websocket connection bytes
+ size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) {
+ // if in state header we are expecting a 0x00 byte, if we don't get one
+ // it is a fatal error
+ size_t p = 0; // bytes processed
+ size_t l = 0;
+
+ ec = lib::error_code();
+
+ while (p < len) {
+ if (m_state == HEADER) {
+ if (buf[p] == msg_hdr) {
+ p++;
+ m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1);
+
+ if (!m_msg_ptr) {
+ ec = make_error_code(websocketpp::error::no_incoming_buffers);
+ m_state = FATAL_ERROR;
+ } else {
+ m_state = PAYLOAD;
+ }
+ } else {
+ ec = make_error_code(error::protocol_violation);
+ m_state = FATAL_ERROR;
+ }
+ } else if (m_state == PAYLOAD) {
+ uint8_t *it = std::find(buf+p,buf+len,msg_ftr);
+
+ // 0 1 2 3 4 5
+ // 0x00 0x23 0x23 0x23 0xff 0xXX
+
+ // Copy payload bytes into message
+ l = static_cast<size_t>(it-(buf+p));
+ m_msg_ptr->append_payload(buf+p,l);
+ p += l;
+
+ if (it != buf+len) {
+ // message is done, copy it and the trailing
+ p++;
+ // TODO: validation
+ m_state = READY;
+ }
+ } else {
+ // TODO
+ break;
+ }
+ }
+ // If we get one, we create a new message and move to application state
+
+ // if in state application we are copying bytes into the output message
+ // and validating them for UTF8 until we hit a 0xff byte. Once we hit
+ // 0x00, the message is complete and is dispatched. Then we go back to
+ // header state.
+
+ //ec = make_error_code(error::not_implemented);
+ return p;
+ }
+
+ bool ready() const {
+ return (m_state == READY);
+ }
+
+ bool get_error() const {
+ return false;
+ }
+
+ message_ptr get_message() {
+ message_ptr ret = m_msg_ptr;
+ m_msg_ptr = message_ptr();
+ m_state = HEADER;
+ return ret;
+ }
+
+ /// Prepare a message for writing
+ /**
+ * Performs validation, masking, compression, etc. will return an error if
+ * there was an error, otherwise msg will be ready to be written
+ */
+ virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
+ {
+ if (!in || !out) {
+ return make_error_code(error::invalid_arguments);
+ }
+
+ // TODO: check if the message is prepared already
+
+ // validate opcode
+ if (in->get_opcode() != frame::opcode::text) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ std::string& i = in->get_raw_payload();
+ //std::string& o = out->get_raw_payload();
+
+ // validate payload utf8
+ if (!utf8_validator::validate(i)) {
+ return make_error_code(error::invalid_payload);
+ }
+
+ // generate header
+ out->set_header(std::string(reinterpret_cast<char const *>(&msg_hdr),1));
+
+ // process payload
+ out->set_payload(i);
+ out->append_payload(std::string(reinterpret_cast<char const *>(&msg_ftr),1));
+
+ // hybi00 doesn't support compression
+ // hybi00 doesn't have masking
+
+ out->set_prepared(true);
+
+ return lib::error_code();
+ }
+
+ /// Prepare a ping frame
+ /**
+ * Hybi 00 doesn't support pings so this will always return an error
+ *
+ * @param in The string to use for the ping payload
+ * @param out The message buffer to prepare the ping in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ lib::error_code prepare_ping(std::string const &, message_ptr) const
+ {
+ return lib::error_code(error::no_protocol_support);
+ }
+
+ /// Prepare a pong frame
+ /**
+ * Hybi 00 doesn't support pongs so this will always return an error
+ *
+ * @param in The string to use for the pong payload
+ * @param out The message buffer to prepare the pong in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ lib::error_code prepare_pong(std::string const &, message_ptr) const
+ {
+ return lib::error_code(error::no_protocol_support);
+ }
+
+ /// Prepare a close frame
+ /**
+ * Hybi 00 doesn't support the close code or reason so these parameters are
+ * ignored.
+ *
+ * @param code The close code to send
+ * @param reason The reason string to send
+ * @param out The message buffer to prepare the fame in
+ * @return Status code, zero on success, non-zero on failure
+ */
+ lib::error_code prepare_close(close::status::value, std::string const &,
+ message_ptr out) const
+ {
+ if (!out) {
+ return lib::error_code(error::invalid_arguments);
+ }
+
+ std::string val;
+ val.append(1,'\xff');
+ val.append(1,'\x00');
+ out->set_payload(val);
+ out->set_prepared(true);
+
+ return lib::error_code();
+ }
+private:
+ void decode_client_key(std::string const & key, char * result) const {
+ unsigned int spaces = 0;
+ std::string digits;
+ uint32_t num;
+
+ // key2
+ for (size_t i = 0; i < key.size(); i++) {
+ if (key[i] == ' ') {
+ spaces++;
+ } else if (key[i] >= '0' && key[i] <= '9') {
+ digits += key[i];
+ }
+ }
+
+ num = static_cast<uint32_t>(strtoul(digits.c_str(), NULL, 10));
+ if (spaces > 0 && num > 0) {
+ num = htonl(num/spaces);
+ std::copy(reinterpret_cast<char*>(&num),
+ reinterpret_cast<char*>(&num)+4,
+ result);
+ } else {
+ std::fill(result,result+4,0);
+ }
+ }
+
+ enum state {
+ HEADER = 0,
+ PAYLOAD = 1,
+ READY = 2,
+ FATAL_ERROR = 3
+ };
+
+ uint8_t const msg_hdr;
+ uint8_t const msg_ftr;
+
+ state m_state;
+
+ msg_manager_ptr m_msg_manager;
+ message_ptr m_msg_ptr;
+ utf8_validator::validator m_validator;
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI00_HPP
diff --git a/websocketpp/processors/hybi07.hpp b/websocketpp/processors/hybi07.hpp
new file mode 100644
index 00000000..14b67c21
--- /dev/null
+++ b/websocketpp/processors/hybi07.hpp
@@ -0,0 +1,78 @@
+/*
+ * 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 WEBSOCKETPP_PROCESSOR_HYBI07_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI07_HPP
+
+#include <websocketpp/processors/hybi08.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi Draft version 07
+/**
+ * The primary difference between 07 and 08 is a version number.
+ */
+template <typename config>
+class hybi07 : public hybi08<config> {
+public:
+ typedef typename config::request_type request_type;
+
+ typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
+ typedef typename config::rng_type rng_type;
+
+ explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
+ : hybi08<config>(secure, p_is_server, manager, rng) {}
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * The Hybi 07 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type &, uri_ptr,
+ std::vector<std::string> const &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ int get_version() const {
+ return 7;
+ }
+private:
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI07_HPP
diff --git a/websocketpp/processors/hybi08.hpp b/websocketpp/processors/hybi08.hpp
new file mode 100644
index 00000000..15f6e658
--- /dev/null
+++ b/websocketpp/processors/hybi08.hpp
@@ -0,0 +1,83 @@
+/*
+ * 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 WEBSOCKETPP_PROCESSOR_HYBI08_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI08_HPP
+
+#include <websocketpp/processors/hybi13.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi Draft version 08
+/**
+ * The primary difference between 08 and 13 is a different origin header name
+ */
+template <typename config>
+class hybi08 : public hybi13<config> {
+public:
+ typedef hybi08<config> type;
+ typedef typename config::request_type request_type;
+
+ typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
+ typedef typename config::rng_type rng_type;
+
+ explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
+ : hybi13<config>(secure, p_is_server, manager, rng) {}
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * The Hybi 08 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type &, uri_ptr,
+ std::vector<std::string> const &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ int get_version() const {
+ return 8;
+ }
+
+ std::string const & get_origin(request_type const & r) const {
+ return r.get_header("Sec-WebSocket-Origin");
+ }
+private:
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI08_HPP
diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp
new file mode 100644
index 00000000..79486654
--- /dev/null
+++ b/websocketpp/processors/hybi13.hpp
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2015, 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 WEBSOCKETPP_PROCESSOR_HYBI13_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI13_HPP
+
+#include <websocketpp/processors/processor.hpp>
+
+#include <websocketpp/frame.hpp>
+#include <websocketpp/http/constants.hpp>
+
+#include <websocketpp/utf8_validator.hpp>
+#include <websocketpp/sha1/sha1.hpp>
+#include <websocketpp/base64/base64.hpp>
+
+#include <websocketpp/common/network.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+#include <vector>
+#include <utility>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi version 13 (RFC6455)
+template <typename config>
+class hybi13 : public processor<config> {
+public:
+ typedef processor<config> base;
+
+ typedef typename config::request_type request_type;
+ typedef typename config::response_type response_type;
+
+ typedef typename config::message_type message_type;
+ typedef typename message_type::ptr message_ptr;
+
+ typedef typename config::con_msg_manager_type msg_manager_type;
+ typedef typename msg_manager_type::ptr msg_manager_ptr;
+ typedef typename config::rng_type rng_type;
+
+ typedef typename config::permessage_deflate_type permessage_deflate_type;
+
+ typedef std::pair<lib::error_code,std::string> err_str_pair;
+
+ explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
+ : processor<config>(secure, p_is_server)
+ , m_msg_manager(manager)
+ , m_rng(rng)
+ {
+ reset_headers();
+ }
+
+ int get_version() const {
+ return 13;
+ }
+
+ bool has_permessage_deflate() const {
+ return m_permessage_deflate.is_implemented();
+ }
+
+ err_str_pair negotiate_extensions(request_type const & request) {
+ return negotiate_extensions_helper(request);
+ }
+
+ err_str_pair negotiate_extensions(response_type const & response) {
+ return negotiate_extensions_helper(response);
+ }
+
+ /// Extension negotiation helper function
+ /**
+ * This exists mostly because the code for requests and responses is
+ * identical and I can't have virtual template methods.
+ */
+ template <typename header_type>
+ err_str_pair negotiate_extensions_helper(header_type const & header) {
+ err_str_pair ret;
+
+ // Respect blanket disabling of all extensions and don't even parse
+ // the extension header
+ if (!config::enable_extensions) {
+ ret.first = make_error_code(error::extensions_disabled);
+ return ret;
+ }
+
+ http::parameter_list p;
+
+ bool error = header.get_header_as_plist("Sec-WebSocket-Extensions",p);
+
+ if (error) {
+ ret.first = make_error_code(error::extension_parse_error);
+ return ret;
+ }
+
+ // If there are no extensions parsed then we are done!
+ if (p.size() == 0) {
+ return ret;
+ }
+
+ http::parameter_list::const_iterator it;
+
+ if (m_permessage_deflate.is_implemented()) {
+ err_str_pair neg_ret;
+ for (it = p.begin(); it != p.end(); ++it) {
+ // look through each extension, if the key is permessage-deflate
+ if (it->first == "permessage-deflate") {
+ // if we have already successfully negotiated this extension
+ // then skip any other requests to negotiate the same one
+ // with different parameters
+ if (m_permessage_deflate.is_enabled()) {
+ continue;
+ }
+
+
+ neg_ret = m_permessage_deflate.negotiate(it->second);
+
+ if (neg_ret.first) {
+ // Figure out if this is an error that should halt all
+ // extension negotiations or simply cause negotiation of
+ // this specific extension to fail.
+ //std::cout << "permessage-compress negotiation failed: "
+ // << neg_ret.first.message() << std::endl;
+ } else {
+ // Note: this list will need commas if WebSocket++ ever
+ // supports more than one extension
+ ret.second += neg_ret.second;
+ m_permessage_deflate.init(base::m_server);
+ continue;
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ lib::error_code validate_handshake(request_type const & r) const {
+ if (r.get_method() != "GET") {
+ return make_error_code(error::invalid_http_method);
+ }
+
+ if (r.get_version() != "HTTP/1.1") {
+ return make_error_code(error::invalid_http_version);
+ }
+
+ // required headers
+ // Host is required by HTTP/1.1
+ // Connection is required by is_websocket_handshake
+ // Upgrade is required by is_websocket_handshake
+ if (r.get_header("Sec-WebSocket-Key").empty()) {
+ return make_error_code(error::missing_required_header);
+ }
+
+ return lib::error_code();
+ }
+
+ /* TODO: the 'subprotocol' parameter may need to be expanded into a more
+ * generic struct if other user input parameters to the processed handshake
+ * are found.
+ */
+ lib::error_code process_handshake(request_type const & request,
+ std::string const & subprotocol, response_type & response) const
+ {
+ std::string server_key = request.get_header("Sec-WebSocket-Key");
+
+ lib::error_code ec = process_handshake_key(server_key);
+
+ if (ec) {
+ return ec;
+ }
+
+ response.replace_header("Sec-WebSocket-Accept",server_key);
+ response.append_header("Upgrade",constants::upgrade_token);
+ response.append_header("Connection",constants::connection_token);
+
+ if (!subprotocol.empty()) {
+ response.replace_header("Sec-WebSocket-Protocol",subprotocol);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type & req, uri_ptr
+ uri, std::vector<std::string> const & subprotocols) const
+ {
+ req.set_method("GET");
+ req.set_uri(uri->get_resource());
+ req.set_version("HTTP/1.1");
+
+ req.append_header("Upgrade","websocket");
+ req.append_header("Connection","Upgrade");
+ req.replace_header("Sec-WebSocket-Version","13");
+ req.replace_header("Host",uri->get_host_port());
+
+ if (!subprotocols.empty()) {
+ std::ostringstream result;
+ std::vector<std::string>::const_iterator it = subprotocols.begin();
+ result << *it++;
+ while (it != subprotocols.end()) {
+ result << ", " << *it++;
+ }
+
+ req.replace_header("Sec-WebSocket-Protocol",result.str());
+ }
+
+ // Generate handshake key
+ frame::uint32_converter conv;
+ unsigned char raw_key[16];
+
+ for (int i = 0; i < 4; i++) {
+ conv.i = m_rng();
+ std::copy(conv.c,conv.c+4,&raw_key[i*4]);
+ }
+
+ req.replace_header("Sec-WebSocket-Key",base64_encode(raw_key, 16));
+
+ if (m_permessage_deflate.is_implemented()) {
+ std::string offer = m_permessage_deflate.generate_offer();
+ if (!offer.empty()) {
+ req.replace_header("Sec-WebSocket-Extensions",offer);
+ }
+ }
+
+ return lib::error_code();
+ }
+
+ /// Validate the server's response to an outgoing handshake request
+ /**
+ * @param req The original request sent
+ * @param res The reponse to generate
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ lib::error_code validate_server_handshake_response(request_type const & req,
+ response_type& res) const
+ {
+ // A valid response has an HTTP 101 switching protocols code
+ if (res.get_status_code() != http::status_code::switching_protocols) {
+ return error::make_error_code(error::invalid_http_status);
+ }
+
+ // And the upgrade token in an upgrade header
+ std::string const & upgrade_header = res.get_header("Upgrade");
+ if (utility::ci_find_substr(upgrade_header, constants::upgrade_token,
+ sizeof(constants::upgrade_token)-1) == upgrade_header.end())
+ {
+ return error::make_error_code(error::missing_required_header);
+ }
+
+ // And the websocket token in the connection header
+ std::string const & con_header = res.get_header("Connection");
+ if (utility::ci_find_substr(con_header, constants::connection_token,
+ sizeof(constants::connection_token)-1) == con_header.end())
+ {
+ return error::make_error_code(error::missing_required_header);
+ }
+
+ // And has a valid Sec-WebSocket-Accept value
+ std::string key = req.get_header("Sec-WebSocket-Key");
+ lib::error_code ec = process_handshake_key(key);
+
+ if (ec || key != res.get_header("Sec-WebSocket-Accept")) {
+ return error::make_error_code(error::missing_required_header);
+ }
+
+ // check extensions
+
+ return lib::error_code();
+ }
+
+ std::string get_raw(response_type const & res) const {
+ return res.raw();
+ }
+
+ std::string const & get_origin(request_type const & r) const {
+ return r.get_header("Origin");
+ }
+
+ lib::error_code extract_subprotocols(request_type const & req,
+ std::vector<std::string> & subprotocol_list)
+ {
+ if (!req.get_header("Sec-WebSocket-Protocol").empty()) {
+ http::parameter_list p;
+
+ if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) {
+ http::parameter_list::const_iterator it;
+
+ for (it = p.begin(); it != p.end(); ++it) {
+ subprotocol_list.push_back(it->first);
+ }
+ } else {
+ return error::make_error_code(error::subprotocol_parse_error);
+ }
+ }
+ return lib::error_code();
+ }
+
+ uri_ptr get_uri(request_type const & request) const {
+ return get_uri_from_host(request,(base::m_secure ? "wss" : "ws"));
+ }
+
+ /// Process new websocket connection bytes
+ /**
+ *
+ * Hybi 13 data streams represent a series of variable length frames. Each
+ * frame is made up of a series of fixed length fields. The lengths of later
+ * fields are contained in earlier fields. The first field length is fixed
+ * by the spec.
+ *
+ * This processor represents a state machine that keeps track of what field
+ * is presently being read and how many more bytes are needed to complete it
+ *
+ *
+ *
+ *
+ * Read two header bytes
+ * Extract full frame length.
+ * Read extra header bytes
+ * Validate frame header (including extension validate)
+ * Read extension data into extension message state object
+ * Read payload data into payload
+ *
+ * @param buf Input buffer
+ *
+ * @param len Length of input buffer
+ *
+ * @return Number of bytes processed or zero on error
+ */
+ size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) {
+ size_t p = 0;
+
+ ec = lib::error_code();
+
+ //std::cout << "consume: " << utility::to_hex(buf,len) << std::endl;
+
+ // Loop while we don't have a message ready and we still have bytes
+ // left to process.
+ while (m_state != READY && m_state != FATAL_ERROR &&
+ (p < len || m_bytes_needed == 0))
+ {
+ if (m_state == HEADER_BASIC) {
+ p += this->copy_basic_header_bytes(buf+p,len-p);
+
+ if (m_bytes_needed > 0) {
+ continue;
+ }
+
+ ec = this->validate_incoming_basic_header(
+ m_basic_header, base::m_server, !m_data_msg.msg_ptr
+ );
+ if (ec) {break;}
+
+ // extract full header size and adjust consume state accordingly
+ m_state = HEADER_EXTENDED;
+ m_cursor = 0;
+ m_bytes_needed = frame::get_header_len(m_basic_header) -
+ frame::BASIC_HEADER_LENGTH;
+ } else if (m_state == HEADER_EXTENDED) {
+ p += this->copy_extended_header_bytes(buf+p,len-p);
+
+ if (m_bytes_needed > 0) {
+ continue;
+ }
+
+ ec = validate_incoming_extended_header(m_basic_header,m_extended_header);
+ if (ec){break;}
+
+ m_state = APPLICATION;
+ m_bytes_needed = static_cast<size_t>(get_payload_size(m_basic_header,m_extended_header));
+
+ // check if this frame is the start of a new message and set up
+ // the appropriate message metadata.
+ frame::opcode::value op = frame::get_opcode(m_basic_header);
+
+ // TODO: get_message failure conditions
+
+ if (frame::opcode::is_control(op)) {
+ m_control_msg = msg_metadata(
+ m_msg_manager->get_message(op,m_bytes_needed),
+ frame::get_masking_key(m_basic_header,m_extended_header)
+ );
+
+ m_current_msg = &m_control_msg;
+ } else {
+ if (!m_data_msg.msg_ptr) {
+ if (m_bytes_needed > base::m_max_message_size) {
+ ec = make_error_code(error::message_too_big);
+ break;
+ }
+
+ m_data_msg = msg_metadata(
+ m_msg_manager->get_message(op,m_bytes_needed),
+ frame::get_masking_key(m_basic_header,m_extended_header)
+ );
+
+ if (m_permessage_deflate.is_enabled()) {
+ m_data_msg.msg_ptr->set_compressed(frame::get_rsv1(m_basic_header));
+ }
+ } else {
+ // Fetch the underlying payload buffer from the data message we
+ // are writing into.
+ std::string & out = m_data_msg.msg_ptr->get_raw_payload();
+
+ if (out.size() + m_bytes_needed > base::m_max_message_size) {
+ ec = make_error_code(error::message_too_big);
+ break;
+ }
+
+ // Each frame starts a new masking key. All other state
+ // remains between frames.
+ m_data_msg.prepared_key = prepare_masking_key(
+ frame::get_masking_key(
+ m_basic_header,
+ m_extended_header
+ )
+ );
+
+ out.reserve(out.size() + m_bytes_needed);
+ }
+ m_current_msg = &m_data_msg;
+ }
+ } else if (m_state == EXTENSION) {
+ m_state = APPLICATION;
+ } else if (m_state == APPLICATION) {
+ size_t bytes_to_process = (std::min)(m_bytes_needed,len-p);
+
+ if (bytes_to_process > 0) {
+ p += this->process_payload_bytes(buf+p,bytes_to_process,ec);
+
+ if (ec) {break;}
+ }
+
+ if (m_bytes_needed > 0) {
+ continue;
+ }
+
+ // If this was the last frame in the message set the ready flag.
+ // Otherwise, reset processor state to read additional frames.
+ if (frame::get_fin(m_basic_header)) {
+ ec = finalize_message();
+ if (ec) {
+ break;
+ }
+ } else {
+ this->reset_headers();
+ }
+ } else {
+ // shouldn't be here
+ ec = make_error_code(error::general);
+ return 0;
+ }
+ }
+
+ return p;
+ }
+
+ /// Perform any finalization actions on an incoming message
+ /**
+ * Called after the full message is received. Provides the opportunity for
+ * extensions to complete any data post processing as well as final UTF8
+ * validation checks for text messages.
+ *
+ * @return A code indicating errors, if any
+ */
+ lib::error_code finalize_message() {
+ std::string & out = m_current_msg->msg_ptr->get_raw_payload();
+
+ // if the frame is compressed, append the compression
+ // trailer and flush the compression buffer.
+ if (m_permessage_deflate.is_enabled()
+ && m_current_msg->msg_ptr->get_compressed())
+ {
+ uint8_t trailer[4] = {0x00, 0x00, 0xff, 0xff};
+
+ // Decompress current buffer into the message buffer
+ lib::error_code ec;
+ ec = m_permessage_deflate.decompress(trailer,4,out);
+ if (ec) {
+ return ec;
+ }
+ }
+
+ // ensure that text messages end on a valid UTF8 code point
+ if (frame::get_opcode(m_basic_header) == frame::opcode::TEXT) {
+ if (!m_current_msg->validator.complete()) {
+ return make_error_code(error::invalid_utf8);
+ }
+ }
+
+ m_state = READY;
+
+ return lib::error_code();
+ }
+
+ void reset_headers() {
+ m_state = HEADER_BASIC;
+ m_bytes_needed = frame::BASIC_HEADER_LENGTH;
+
+ m_basic_header.b0 = 0x00;
+ m_basic_header.b1 = 0x00;
+
+ std::fill_n(
+ m_extended_header.bytes,
+ frame::MAX_EXTENDED_HEADER_LENGTH,
+ 0x00
+ );
+ }
+
+ /// Test whether or not the processor has a message ready
+ bool ready() const {
+ return (m_state == READY);
+ }
+
+ message_ptr get_message() {
+ if (!ready()) {
+ return message_ptr();
+ }
+ message_ptr ret = m_current_msg->msg_ptr;
+ m_current_msg->msg_ptr.reset();
+
+ if (frame::opcode::is_control(ret->get_opcode())) {
+ m_control_msg.msg_ptr.reset();
+ } else {
+ m_data_msg.msg_ptr.reset();
+ }
+
+ this->reset_headers();
+
+ return ret;
+ }
+
+ /// Test whether or not the processor is in a fatal error state.
+ bool get_error() const {
+ return m_state == FATAL_ERROR;
+ }
+
+ size_t get_bytes_needed() const {
+ return m_bytes_needed;
+ }
+
+ /// Prepare a user data message for writing
+ /**
+ * Performs validation, masking, compression, etc. will return an error if
+ * there was an error, otherwise msg will be ready to be written
+ *
+ * TODO: tests
+ *
+ * @param in An unprepared message to prepare
+ * @param out A message to be overwritten with the prepared message
+ * @return error code
+ */
+ virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
+ {
+ if (!in || !out) {
+ return make_error_code(error::invalid_arguments);
+ }
+
+ frame::opcode::value op = in->get_opcode();
+
+ // validate opcode: only regular data frames
+ if (frame::opcode::is_control(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ std::string& i = in->get_raw_payload();
+ std::string& o = out->get_raw_payload();
+
+ // validate payload utf8
+ if (op == frame::opcode::TEXT && !utf8_validator::validate(i)) {
+ return make_error_code(error::invalid_payload);
+ }
+
+ frame::masking_key_type key;
+ bool masked = !base::m_server;
+ bool compressed = m_permessage_deflate.is_enabled()
+ && in->get_compressed();
+ bool fin = in->get_fin();
+
+ if (masked) {
+ // Generate masking key.
+ key.i = m_rng();
+ } else {
+ key.i = 0;
+ }
+
+ // prepare payload
+ if (compressed) {
+ // compress and store in o after header.
+ m_permessage_deflate.compress(i,o);
+
+ if (o.size() < 4) {
+ return make_error_code(error::general);
+ }
+
+ // Strip trailing 4 0x00 0x00 0xff 0xff bytes before writing to the
+ // wire
+ o.resize(o.size()-4);
+
+ // mask in place if necessary
+ if (masked) {
+ this->masked_copy(o,o,key);
+ }
+ } else {
+ // no compression, just copy data into the output buffer
+ o.resize(i.size());
+
+ // if we are masked, have the masking function write to the output
+ // buffer directly to avoid another copy. If not masked, copy
+ // directly without masking.
+ if (masked) {
+ this->masked_copy(i,o,key);
+ } else {
+ std::copy(i.begin(),i.end(),o.begin());
+ }
+ }
+
+ // generate header
+ frame::basic_header h(op,o.size(),fin,masked,compressed);
+
+ if (masked) {
+ frame::extended_header e(o.size(),key.i);
+ out->set_header(frame::prepare_header(h,e));
+ } else {
+ frame::extended_header e(o.size());
+ out->set_header(frame::prepare_header(h,e));
+ }
+
+ out->set_prepared(true);
+ out->set_opcode(op);
+
+ return lib::error_code();
+ }
+
+ /// Get URI
+ lib::error_code prepare_ping(std::string const & in, message_ptr out) const {
+ return this->prepare_control(frame::opcode::PING,in,out);
+ }
+
+ lib::error_code prepare_pong(std::string const & in, message_ptr out) const {
+ return this->prepare_control(frame::opcode::PONG,in,out);
+ }
+
+ virtual lib::error_code prepare_close(close::status::value code,
+ std::string const & reason, message_ptr out) const
+ {
+ if (close::status::reserved(code)) {
+ return make_error_code(error::reserved_close_code);
+ }
+
+ if (close::status::invalid(code) && code != close::status::no_status) {
+ return make_error_code(error::invalid_close_code);
+ }
+
+ if (code == close::status::no_status && reason.size() > 0) {
+ return make_error_code(error::reason_requires_code);
+ }
+
+ if (reason.size() > frame:: limits::payload_size_basic-2) {
+ return make_error_code(error::control_too_big);
+ }
+
+ std::string payload;
+
+ if (code != close::status::no_status) {
+ close::code_converter val;
+ val.i = htons(code);
+
+ payload.resize(reason.size()+2);
+
+ payload[0] = val.c[0];
+ payload[1] = val.c[1];
+
+ std::copy(reason.begin(),reason.end(),payload.begin()+2);
+ }
+
+ return this->prepare_control(frame::opcode::CLOSE,payload,out);
+ }
+protected:
+ /// Convert a client handshake key into a server response key in place
+ lib::error_code process_handshake_key(std::string & key) const {
+ key.append(constants::handshake_guid);
+
+ unsigned char message_digest[20];
+ sha1::calc(key.c_str(),key.length(),message_digest);
+ key = base64_encode(message_digest,20);
+
+ return lib::error_code();
+ }
+
+ /// Reads bytes from buf into m_basic_header
+ size_t copy_basic_header_bytes(uint8_t const * buf, size_t len) {
+ if (len == 0 || m_bytes_needed == 0) {
+ return 0;
+ }
+
+ if (len > 1) {
+ // have at least two bytes
+ if (m_bytes_needed == 2) {
+ m_basic_header.b0 = buf[0];
+ m_basic_header.b1 = buf[1];
+ m_bytes_needed -= 2;
+ return 2;
+ } else {
+ m_basic_header.b1 = buf[0];
+ m_bytes_needed--;
+ return 1;
+ }
+ } else {
+ // have exactly one byte
+ if (m_bytes_needed == 2) {
+ m_basic_header.b0 = buf[0];
+ m_bytes_needed--;
+ return 1;
+ } else {
+ m_basic_header.b1 = buf[0];
+ m_bytes_needed--;
+ return 1;
+ }
+ }
+ }
+
+ /// Reads bytes from buf into m_extended_header
+ size_t copy_extended_header_bytes(uint8_t const * buf, size_t len) {
+ size_t bytes_to_read = (std::min)(m_bytes_needed,len);
+
+ std::copy(buf,buf+bytes_to_read,m_extended_header.bytes+m_cursor);
+ m_cursor += bytes_to_read;
+ m_bytes_needed -= bytes_to_read;
+
+ return bytes_to_read;
+ }
+
+ /// Reads bytes from buf into message payload
+ /**
+ * This function performs unmasking and uncompression, validates the
+ * decoded bytes, and writes them to the appropriate message buffer.
+ *
+ * This member function will use the input buffer as stratch space for its
+ * work. The raw input bytes will not be preserved. This applies only to the
+ * bytes actually needed. At most min(m_bytes_needed,len) will be processed.
+ *
+ * @param buf Input/working buffer
+ * @param len Length of buf
+ * @return Number of bytes processed or zero in case of an error
+ */
+ size_t process_payload_bytes(uint8_t * buf, size_t len, lib::error_code& ec)
+ {
+ // unmask if masked
+ if (frame::get_masked(m_basic_header)) {
+ m_current_msg->prepared_key = frame::byte_mask_circ(
+ buf, len, m_current_msg->prepared_key);
+ // TODO: SIMD masking
+ }
+
+ std::string & out = m_current_msg->msg_ptr->get_raw_payload();
+ size_t offset = out.size();
+
+ // decompress message if needed.
+ if (m_permessage_deflate.is_enabled()
+ && m_current_msg->msg_ptr->get_compressed())
+ {
+ // Decompress current buffer into the message buffer
+ ec = m_permessage_deflate.decompress(buf,len,out);
+ if (ec) {
+ return 0;
+ }
+ } else {
+ // No compression, straight copy
+ out.append(reinterpret_cast<char *>(buf),len);
+ }
+
+ // validate unmasked, decompressed values
+ if (m_current_msg->msg_ptr->get_opcode() == frame::opcode::TEXT) {
+ if (!m_current_msg->validator.decode(out.begin()+offset,out.end())) {
+ ec = make_error_code(error::invalid_utf8);
+ return 0;
+ }
+ }
+
+ m_bytes_needed -= len;
+
+ return len;
+ }
+
+ /// Validate an incoming basic header
+ /**
+ * Validates an incoming hybi13 basic header.
+ *
+ * @param h The basic header to validate
+ * @param is_server Whether or not the endpoint that received this frame
+ * is a server.
+ * @param new_msg Whether or not this is the first frame of the message
+ * @return 0 on success or a non-zero error code on failure
+ */
+ lib::error_code validate_incoming_basic_header(frame::basic_header const & h,
+ bool is_server, bool new_msg) const
+ {
+ frame::opcode::value op = frame::get_opcode(h);
+
+ // Check control frame size limit
+ if (frame::opcode::is_control(op) &&
+ frame::get_basic_size(h) > frame::limits::payload_size_basic)
+ {
+ return make_error_code(error::control_too_big);
+ }
+
+ // Check that RSV bits are clear
+ // The only RSV bits allowed are rsv1 if the permessage_compress
+ // extension is enabled for this connection and the message is not
+ // a control message.
+ //
+ // TODO: unit tests for this
+ if (frame::get_rsv1(h) && (!m_permessage_deflate.is_enabled()
+ || frame::opcode::is_control(op)))
+ {
+ return make_error_code(error::invalid_rsv_bit);
+ }
+
+ if (frame::get_rsv2(h) || frame::get_rsv3(h)) {
+ return make_error_code(error::invalid_rsv_bit);
+ }
+
+ // Check for reserved opcodes
+ if (frame::opcode::reserved(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ // Check for invalid opcodes
+ // TODO: unit tests for this?
+ if (frame::opcode::invalid(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ // Check for fragmented control message
+ if (frame::opcode::is_control(op) && !frame::get_fin(h)) {
+ return make_error_code(error::fragmented_control);
+ }
+
+ // Check for continuation without an active message
+ if (new_msg && op == frame::opcode::CONTINUATION) {
+ return make_error_code(error::invalid_continuation);
+ }
+
+ // Check for new data frame when expecting continuation
+ if (!new_msg && !frame::opcode::is_control(op) &&
+ op != frame::opcode::CONTINUATION)
+ {
+ return make_error_code(error::invalid_continuation);
+ }
+
+ // Servers should reject any unmasked frames from clients.
+ // Clients should reject any masked frames from servers.
+ if (is_server && !frame::get_masked(h)) {
+ return make_error_code(error::masking_required);
+ } else if (!is_server && frame::get_masked(h)) {
+ return make_error_code(error::masking_forbidden);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Validate an incoming extended header
+ /**
+ * Validates an incoming hybi13 full header.
+ *
+ * @todo unit test for the >32 bit frames on 32 bit systems case
+ *
+ * @param h The basic header to validate
+ * @param e The extended header to validate
+ * @return An error_code, non-zero values indicate why the validation
+ * failed
+ */
+ lib::error_code validate_incoming_extended_header(frame::basic_header h,
+ frame::extended_header e) const
+ {
+ uint8_t basic_size = frame::get_basic_size(h);
+ uint64_t payload_size = frame::get_payload_size(h,e);
+
+ // Check for non-minimally encoded payloads
+ if (basic_size == frame::payload_size_code_16bit &&
+ payload_size <= frame::limits::payload_size_basic)
+ {
+ return make_error_code(error::non_minimal_encoding);
+ }
+
+ if (basic_size == frame::payload_size_code_64bit &&
+ payload_size <= frame::limits::payload_size_extended)
+ {
+ return make_error_code(error::non_minimal_encoding);
+ }
+
+ // Check for >32bit frames on 32 bit systems
+ if (sizeof(size_t) == 4 && (payload_size >> 32)) {
+ return make_error_code(error::requires_64bit);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Copy and mask/unmask in one operation
+ /**
+ * Reads input from one string and writes unmasked output to another.
+ *
+ * @param [in] i The input string.
+ * @param [out] o The output string.
+ * @param [in] key The masking key to use for masking/unmasking
+ */
+ void masked_copy (std::string const & i, std::string & o,
+ frame::masking_key_type key) const
+ {
+ frame::byte_mask(i.begin(),i.end(),o.begin(),key);
+ // TODO: SIMD masking
+ }
+
+ /// Generic prepare control frame with opcode and payload.
+ /**
+ * Internal control frame building method. Handles validation, masking, etc
+ *
+ * @param op The control opcode to use
+ * @param payload The payload to use
+ * @param out The message buffer to store the prepared frame in
+ * @return Status code, zero on success, non-zero on error
+ */
+ lib::error_code prepare_control(frame::opcode::value op,
+ std::string const & payload, message_ptr out) const
+ {
+ if (!out) {
+ return make_error_code(error::invalid_arguments);
+ }
+
+ if (!frame::opcode::is_control(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ if (payload.size() > frame::limits::payload_size_basic) {
+ return make_error_code(error::control_too_big);
+ }
+
+ frame::masking_key_type key;
+ bool masked = !base::m_server;
+
+ frame::basic_header h(op,payload.size(),true,masked);
+
+ std::string & o = out->get_raw_payload();
+ o.resize(payload.size());
+
+ if (masked) {
+ // Generate masking key.
+ key.i = m_rng();
+
+ frame::extended_header e(payload.size(),key.i);
+ out->set_header(frame::prepare_header(h,e));
+ this->masked_copy(payload,o,key);
+ } else {
+ frame::extended_header e(payload.size());
+ out->set_header(frame::prepare_header(h,e));
+ std::copy(payload.begin(),payload.end(),o.begin());
+ }
+
+ out->set_opcode(op);
+ out->set_prepared(true);
+
+ return lib::error_code();
+ }
+
+ enum state {
+ HEADER_BASIC = 0,
+ HEADER_EXTENDED = 1,
+ EXTENSION = 2,
+ APPLICATION = 3,
+ READY = 4,
+ FATAL_ERROR = 5
+ };
+
+ /// This data structure holds data related to processing a message, such as
+ /// the buffer it is being written to, its masking key, its UTF8 validation
+ /// state, and sometimes its compression state.
+ struct msg_metadata {
+ msg_metadata() {}
+ msg_metadata(message_ptr m, size_t p) : msg_ptr(m),prepared_key(p) {}
+ msg_metadata(message_ptr m, frame::masking_key_type p)
+ : msg_ptr(m)
+ , prepared_key(prepare_masking_key(p)) {}
+
+ message_ptr msg_ptr; // pointer to the message data buffer
+ size_t prepared_key; // prepared masking key
+ utf8_validator::validator validator; // utf8 validation state
+ };
+
+ // Basic header of the frame being read
+ frame::basic_header m_basic_header;
+
+ // Pointer to a manager that can create message buffers for us.
+ msg_manager_ptr m_msg_manager;
+
+ // Number of bytes needed to complete the current operation
+ size_t m_bytes_needed;
+
+ // Number of extended header bytes read
+ size_t m_cursor;
+
+ // Metadata for the current data msg
+ msg_metadata m_data_msg;
+ // Metadata for the current control msg
+ msg_metadata m_control_msg;
+
+ // Pointer to the metadata associated with the frame being read
+ msg_metadata * m_current_msg;
+
+ // Extended header of current frame
+ frame::extended_header m_extended_header;
+
+ rng_type & m_rng;
+
+ // Overall state of the processor
+ state m_state;
+
+ // Extensions
+ permessage_deflate_type m_permessage_deflate;
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI13_HPP
diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp
new file mode 100644
index 00000000..5131cc45
--- /dev/null
+++ b/websocketpp/processors/processor.hpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2015, 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 WEBSOCKETPP_PROCESSOR_HPP
+#define WEBSOCKETPP_PROCESSOR_HPP
+
+#include <websocketpp/processors/base.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <websocketpp/close.hpp>
+#include <websocketpp/utilities.hpp>
+#include <websocketpp/uri.hpp>
+
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace websocketpp {
+/// Processors encapsulate the protocol rules specific to each WebSocket version
+/**
+ * The processors namespace includes a number of free functions that operate on
+ * various WebSocket related data structures and perform processing that is not
+ * related to specific versions of the protocol.
+ *
+ * It also includes the abstract interface for the protocol specific processing
+ * engines. These engines wrap all of the logic necessary for parsing and
+ * validating WebSocket handshakes and messages of specific protocol version
+ * and set of allowed extensions.
+ *
+ * An instance of a processor represents the state of a single WebSocket
+ * connection of the associated version. One processor instance is needed per
+ * logical WebSocket connection.
+ */
+namespace processor {
+
+/// Determine whether or not a generic HTTP request is a WebSocket handshake
+/**
+ * @param r The HTTP request to read.
+ *
+ * @return True if the request is a WebSocket handshake, false otherwise
+ */
+template <typename request_type>
+bool is_websocket_handshake(request_type& r) {
+ using utility::ci_find_substr;
+
+ std::string const & upgrade_header = r.get_header("Upgrade");
+
+ if (ci_find_substr(upgrade_header, constants::upgrade_token,
+ sizeof(constants::upgrade_token)-1) == upgrade_header.end())
+ {
+ return false;
+ }
+
+ std::string const & con_header = r.get_header("Connection");
+
+ if (ci_find_substr(con_header, constants::connection_token,
+ sizeof(constants::connection_token)-1) == con_header.end())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/// Extract the version from a WebSocket handshake request
+/**
+ * A blank version header indicates a spec before versions were introduced.
+ * The only such versions in shipping products are Hixie Draft 75 and Hixie
+ * Draft 76. Draft 75 is present in Chrome 4-5 and Safari 5.0.0, Draft 76 (also
+ * known as hybi 00 is present in Chrome 6-13 and Safari 5.0.1+. As
+ * differentiating between these two sets of browsers is very difficult and
+ * Safari 5.0.1+ accounts for the vast majority of cases in the wild this
+ * function assumes that all handshakes without a valid version header are
+ * Hybi 00.
+ *
+ * @param r The WebSocket handshake request to read.
+ *
+ * @return The WebSocket handshake version or -1 if there was an extraction
+ * error.
+ */
+template <typename request_type>
+int get_websocket_version(request_type& r) {
+ if (!r.ready()) {
+ return -2;
+ }
+
+ if (r.get_header("Sec-WebSocket-Version").empty()) {
+ return 0;
+ }
+
+ int version;
+ std::istringstream ss(r.get_header("Sec-WebSocket-Version"));
+
+ if ((ss >> version).fail()) {
+ return -1;
+ }
+
+ return version;
+}
+
+/// Extract a URI ptr from the host header of the request
+/**
+ * @param request The request to extract the Host header from.
+ *
+ * @param scheme The scheme under which this request was received (ws, wss,
+ * http, https, etc)
+ *
+ * @return A uri_pointer that encodes the value of the host header.
+ */
+template <typename request_type>
+uri_ptr get_uri_from_host(request_type & request, std::string scheme) {
+ std::string h = request.get_header("Host");
+
+ size_t last_colon = h.rfind(":");
+ size_t last_sbrace = h.rfind("]");
+
+ // no : = hostname with no port
+ // last : before ] = ipv6 literal with no port
+ // : with no ] = hostname with port
+ // : after ] = ipv6 literal with port
+ if (last_colon == std::string::npos ||
+ (last_sbrace != std::string::npos && last_sbrace > last_colon))
+ {
+ return lib::make_shared<uri>(scheme, h, request.get_uri());
+ } else {
+ return lib::make_shared<uri>(scheme,
+ h.substr(0,last_colon),
+ h.substr(last_colon+1),
+ request.get_uri());
+ }
+}
+
+/// WebSocket protocol processor abstract base class
+template <typename config>
+class processor {
+public:
+ typedef processor<config> type;
+ typedef typename config::request_type request_type;
+ typedef typename config::response_type response_type;
+ typedef typename config::message_type::ptr message_ptr;
+ typedef std::pair<lib::error_code,std::string> err_str_pair;
+
+ explicit processor(bool secure, bool p_is_server)
+ : m_secure(secure)
+ , m_server(p_is_server)
+ , m_max_message_size(config::max_message_size)
+ {}
+
+ virtual ~processor() {}
+
+ /// Get the protocol version of this processor
+ virtual int get_version() const = 0;
+
+ /// Get maximum message size
+ /**
+ * Get maximum message size. Maximum message size determines the point at which the
+ * processor will fail a connection with the message_too_big protocol error.
+ *
+ * The default is retrieved from the max_message_size value from the template config
+ *
+ * @since 0.3.0
+ */
+ size_t get_max_message_size() const {
+ return m_max_message_size;
+ }
+
+ /// Set maximum message size
+ /**
+ * Set maximum message size. Maximum message size determines the point at which the
+ * processor will fail a connection with the message_too_big protocol error.
+ *
+ * The default is retrieved from the max_message_size value from the template config
+ *
+ * @since 0.3.0
+ *
+ * @param new_value The value to set as the maximum message size.
+ */
+ void set_max_message_size(size_t new_value) {
+ m_max_message_size = new_value;
+ }
+
+ /// Returns whether or not the permessage_compress extension is implemented
+ /**
+ * Compile time flag that indicates whether this processor has implemented
+ * the permessage_compress extension. By default this is false.
+ */
+ virtual bool has_permessage_compress() const {
+ return false;
+ }
+
+ /// Initializes extensions based on the Sec-WebSocket-Extensions header
+ /**
+ * Reads the Sec-WebSocket-Extensions header and determines if any of the
+ * requested extensions are supported by this processor. If they are their
+ * settings data is initialized and an extension string to send to the
+ * is returned.
+ *
+ * @param request The request or response headers to look at.
+ */
+ virtual err_str_pair negotiate_extensions(request_type const &) {
+ return err_str_pair();
+ }
+
+ /// Initializes extensions based on the Sec-WebSocket-Extensions header
+ /**
+ * Reads the Sec-WebSocket-Extensions header and determines if any of the
+ * requested extensions were accepted by the server. If they are their
+ * settings data is initialized. If they are not a list of required
+ * extensions (if any) is returned. This list may be sent back to the server
+ * as a part of the 1010/Extension required close code.
+ *
+ * @param response The request or response headers to look at.
+ */
+ virtual err_str_pair negotiate_extensions(response_type const &) {
+ return err_str_pair();
+ }
+
+ /// validate a WebSocket handshake request for this version
+ /**
+ * @param request The WebSocket handshake request to validate.
+ * is_websocket_handshake(request) must be true and
+ * get_websocket_version(request) must equal this->get_version().
+ *
+ * @return A status code, 0 on success, non-zero for specific sorts of
+ * failure
+ */
+ virtual lib::error_code validate_handshake(request_type const & request) const = 0;
+
+ /// Calculate the appropriate response for this websocket request
+ /**
+ * @param req The request to process
+ *
+ * @param subprotocol The subprotocol in use
+ *
+ * @param res The response to store the processed response in
+ *
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ virtual lib::error_code process_handshake(request_type const & req,
+ std::string const & subprotocol, response_type& res) const = 0;
+
+ /// Fill in an HTTP request for an outgoing connection handshake
+ /**
+ * @param req The request to process.
+ *
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ virtual lib::error_code client_handshake_request(request_type & req,
+ uri_ptr uri, std::vector<std::string> const & subprotocols) const = 0;
+
+ /// Validate the server's response to an outgoing handshake request
+ /**
+ * @param req The original request sent
+ * @param res The reponse to generate
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ virtual lib::error_code validate_server_handshake_response(request_type
+ const & req, response_type & res) const = 0;
+
+ /// Given a completed response, get the raw bytes to put on the wire
+ virtual std::string get_raw(response_type const & request) const = 0;
+
+ /// Return the value of the header containing the CORS origin.
+ virtual std::string const & get_origin(request_type const & request) const = 0;
+
+ /// Extracts requested subprotocols from a handshake request
+ /**
+ * Extracts a list of all subprotocols that the client has requested in the
+ * given opening handshake request.
+ *
+ * @param [in] req The request to extract from
+ * @param [out] subprotocol_list A reference to a vector of strings to store
+ * the results in.
+ */
+ virtual lib::error_code extract_subprotocols(const request_type & req,
+ std::vector<std::string> & subprotocol_list) = 0;
+
+ /// Extracts client uri from a handshake request
+ virtual uri_ptr get_uri(request_type const & request) const = 0;
+
+ /// process new websocket connection bytes
+ /**
+ * WebSocket connections are a continous stream of bytes that must be
+ * interpreted by a protocol processor into discrete frames.
+ *
+ * @param buf Buffer from which bytes should be read.
+ * @param len Length of buffer
+ * @param ec Reference to an error code to return any errors in
+ * @return Number of bytes processed
+ */
+ virtual size_t consume(uint8_t *buf, size_t len, lib::error_code & ec) = 0;
+
+ /// Checks if there is a message ready
+ /**
+ * Checks if the most recent consume operation processed enough bytes to
+ * complete a new WebSocket message. The message can be retrieved by calling
+ * get_message() which will reset the internal state to not-ready and allow
+ * consume to read more bytes.
+ *
+ * @return Whether or not a message is ready.
+ */
+ virtual bool ready() const = 0;
+
+ /// Retrieves the most recently processed message
+ /**
+ * Retrieves a shared pointer to the recently completed message if there is
+ * one. If ready() returns true then there is a message available.
+ * Retrieving the message with get_message will reset the state of ready.
+ * As such, each new message may be retrieved only once. Calling get_message
+ * when there is no message available will result in a null pointer being
+ * returned.
+ *
+ * @return A pointer to the most recently processed message or a null shared
+ * pointer.
+ */
+ virtual message_ptr get_message() = 0;
+
+ /// Tests whether the processor is in a fatal error state
+ virtual bool get_error() const = 0;
+
+ /// Retrieves the number of bytes presently needed by the processor
+ /// This value may be used as a hint to the transport layer as to how many
+ /// bytes to wait for before running consume again.
+ virtual size_t get_bytes_needed() const {
+ return 1;
+ }
+
+ /// Prepare a data message for writing
+ /**
+ * Performs validation, masking, compression, etc. will return an error if
+ * there was an error, otherwise msg will be ready to be written
+ */
+ virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0;
+
+ /// Prepare a ping frame
+ /**
+ * Ping preparation is entirely state free. There is no payload validation
+ * other than length. Payload need not be UTF-8.
+ *
+ * @param in The string to use for the ping payload
+ * @param out The message buffer to prepare the ping in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const
+ = 0;
+
+ /// Prepare a pong frame
+ /**
+ * Pong preparation is entirely state free. There is no payload validation
+ * other than length. Payload need not be UTF-8.
+ *
+ * @param in The string to use for the pong payload
+ * @param out The message buffer to prepare the pong in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const
+ = 0;
+
+ /// Prepare a close frame
+ /**
+ * Close preparation is entirely state free. The code and reason are both
+ * subject to validation. Reason must be valid UTF-8. Code must be a valid
+ * un-reserved WebSocket close code. Use close::status::no_status to
+ * indicate no code. If no code is supplied a reason may not be specified.
+ *
+ * @param code The close code to send
+ * @param reason The reason string to send
+ * @param out The message buffer to prepare the fame in
+ * @return Status code, zero on success, non-zero on failure
+ */
+ virtual lib::error_code prepare_close(close::status::value code,
+ std::string const & reason, message_ptr out) const = 0;
+protected:
+ bool const m_secure;
+ bool const m_server;
+ size_t m_max_message_size;
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HPP