/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include namespace websocketpp { namespace processor { /// Processor for Hybi version 13 (RFC6455) template class hybi13 : public processor { public: typedef processor 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 err_str_pair; explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) : processor(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 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 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::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 & 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(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(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