| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007 |
- /*
- * 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_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 & req) {
- 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 = req.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") {
- 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;
- 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") == "") {
- 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));
- 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);
- }
- 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)
- );
- } 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)) {
- // 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()) {
- ec = make_error_code(error::invalid_utf8);
- break;
- }
- }
- m_state = READY;
- } else {
- this->reset_headers();
- }
- } else {
- // shouldn't be here
- ec = make_error_code(error::general);
- return 0;
- }
- }
- return p;
- }
- 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
- *
- * By default WebSocket++ performs block masking/unmasking in a manner that
- * makes assumptions about the nature of the machine and STL library used.
- * In particular the assumption is either a 32 or 64 bit word size and an
- * STL with std::string::data returning a contiguous char array.
- *
- * This method improves masking performance by 3-8x depending on the ratio
- * of small to large messages and the availability of a 64 bit processor.
- *
- * To disable this optimization (for use with alternative STL
- * implementations or processors) define WEBSOCKETPP_STRICT_MASKING when
- * compiling the library. This will force the library to perform masking in
- * single byte chunks.
- *
- * 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();
- // generate header
- frame::basic_header h(op,i.size(),fin,masked,compressed);
- if (masked) {
- // Generate masking key.
- key.i = m_rng();
- frame::extended_header e(i.size(),key.i);
- out->set_header(frame::prepare_header(h,e));
- } else {
- frame::extended_header e(i.size());
- out->set_header(frame::prepare_header(h,e));
- key.i = 0;
- }
- // prepare payload
- if (compressed) {
- // compress and store in o after header.
- m_permessage_deflate.compress(i,o);
- // 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());
- }
- }
- 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)) {
- #ifdef WEBSOCKETPP_STRICT_MASKING
- m_current_msg->prepared_key = frame::byte_mask_circ(
- buf,
- len,
- m_current_msg->prepared_key
- );
- #else
- m_current_msg->prepared_key = frame::word_mask_circ(
- buf,
- len,
- m_current_msg->prepared_key
- );
- #endif
- }
- 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()
- && frame::get_rsv1(m_basic_header))
- {
- // Decompress current buffer into the message buffer
- m_permessage_deflate.decompress(buf,len,out);
- // get the length of the newly uncompressed output
- offset = out.size() - offset;
- } 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
- {
- #ifdef WEBSOCKETPP_STRICT_MASKING
- frame::byte_mask(i.begin(),i.end(),o.begin(),key);
- #else
- websocketpp::frame::word_mask_exact(
- reinterpret_cast<uint8_t *>(const_cast<char *>(i.data())),
- reinterpret_cast<uint8_t *>(const_cast<char *>(o.data())),
- i.size(),
- key
- );
- #endif
- }
- /// 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
|