mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge commit '09987d0f9d32e860f1391bb9c75b799501e2d141' as 'Subtrees/websocket'
This commit is contained in:
630
Subtrees/websocket/src/processors/hybi.hpp
Normal file
630
Subtrees/websocket/src/processors/hybi.hpp
Normal file
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
|
||||
#include "processor.hpp"
|
||||
#include "hybi_header.hpp"
|
||||
|
||||
#include "../base64/base64.h"
|
||||
#include "../sha1/sha1.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#ifdef IGNORE
|
||||
#undef IGNORE
|
||||
#endif // #ifdef IGNORE
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif // #ifdef min
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
namespace hybi_state {
|
||||
enum value {
|
||||
READ_HEADER = 0,
|
||||
READ_PAYLOAD = 1,
|
||||
READY = 2,
|
||||
IGNORE = 3
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
client case
|
||||
end user asks connection for next message
|
||||
- connection returns the next avaliable message or throws if none are ready
|
||||
- connection resets the message and fills in a new masking key
|
||||
end user calls set payload with const ref string to final payload
|
||||
- set opcode... argument to set payload?
|
||||
- set payload checks utf8 if copies from source, masks, and stores in payload.
|
||||
|
||||
|
||||
prepare (hybi):
|
||||
- writes header (writes opcode, payload length, masking key, fin bit, mask bit)
|
||||
|
||||
|
||||
server case
|
||||
end user asks connection for next message
|
||||
- connection returns the next avaliable message or throws if none are ready
|
||||
- connection resets the message and sets masking to off.
|
||||
end user calls set payload with const ref string to final payload
|
||||
- std::copy msg to payload
|
||||
|
||||
prepare
|
||||
- writes header (writes opcode, payload length, fin bit, mask bit)
|
||||
|
||||
|
||||
int reference_count
|
||||
std::list< std::pair< std::string,std::string > >
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*class hybi_message {
|
||||
public:
|
||||
hybi_message(frame::opcode::value opcode) : m_processed(false) {
|
||||
|
||||
}
|
||||
|
||||
void reset() {
|
||||
|
||||
}
|
||||
private:
|
||||
bool m_processed;
|
||||
std::string m_header;
|
||||
std::string m_payload;
|
||||
};*/
|
||||
|
||||
// connection must provide:
|
||||
// int32_t get_rng();
|
||||
// message::data_ptr get_data_message();
|
||||
// message::control_ptr get_control_message();
|
||||
// bool is_secure();
|
||||
|
||||
template <class connection_type>
|
||||
class hybi : public processor_base {
|
||||
public:
|
||||
hybi(connection_type &connection)
|
||||
: m_connection(connection),
|
||||
m_write_frame(connection)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void validate_handshake(const http::parser::request& request) const {
|
||||
std::stringstream err;
|
||||
std::string h;
|
||||
|
||||
if (request.method() != "GET") {
|
||||
err << "Websocket handshake has invalid method: "
|
||||
<< request.method();
|
||||
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
// TODO: allow versions greater than 1.1
|
||||
if (request.version() != "HTTP/1.1") {
|
||||
err << "Websocket handshake has invalid HTTP version: "
|
||||
<< request.method();
|
||||
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
// verify the presence of required headers
|
||||
if (request.header("Host") == "") {
|
||||
throw(http::exception("Required Host header is missing",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
h = request.header("Upgrade");
|
||||
if (h == "") {
|
||||
throw(http::exception("Required Upgrade header is missing",http::status_code::BAD_REQUEST));
|
||||
} else if (!boost::ifind_first(h,"websocket")) {
|
||||
err << "Upgrade header \"" << h << "\", does not contain required token \"websocket\"";
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
h = request.header("Connection");
|
||||
if (h == "") {
|
||||
throw(http::exception("Required Connection header is missing",http::status_code::BAD_REQUEST));
|
||||
} else if (!boost::ifind_first(h,"upgrade")) {
|
||||
err << "Connection header, \"" << h
|
||||
<< "\", does not contain required token \"upgrade\"";
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
if (request.header("Sec-WebSocket-Key") == "") {
|
||||
throw(http::exception("Required Sec-WebSocket-Key header is missing",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
h = request.header("Sec-WebSocket-Version");
|
||||
if (h == "") {
|
||||
throw(http::exception("Required Sec-WebSocket-Version header is missing",http::status_code::BAD_REQUEST));
|
||||
} else {
|
||||
int version = atoi(h.c_str());
|
||||
|
||||
if (version != 7 && version != 8 && version != 13) {
|
||||
err << "This processor doesn't support WebSocket protocol version "
|
||||
<< version;
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_origin(const http::parser::request& request) const {
|
||||
std::string h = request.header("Sec-WebSocket-Version");
|
||||
int version = atoi(h.c_str());
|
||||
|
||||
if (version == 13) {
|
||||
return request.header("Origin");
|
||||
} else if (version == 7 || version == 8) {
|
||||
return request.header("Sec-WebSocket-Origin");
|
||||
} else {
|
||||
throw(http::exception("Could not determine origin header. Check Sec-WebSocket-Version header",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
|
||||
uri_ptr get_uri(const http::parser::request& request) const {
|
||||
std::string h = request.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 uri_ptr(new uri(m_connection.is_secure(),h,request.uri()));
|
||||
} else {
|
||||
return uri_ptr(new uri(m_connection.is_secure(),
|
||||
h.substr(0,last_colon),
|
||||
h.substr(last_colon+1),
|
||||
request.uri()));
|
||||
}
|
||||
|
||||
// TODO: check if get_uri is a full uri
|
||||
}
|
||||
|
||||
void handshake_response(const http::parser::request& request,
|
||||
http::parser::response& response)
|
||||
{
|
||||
std::string server_key = request.header("Sec-WebSocket-Key");
|
||||
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
SHA1 sha;
|
||||
uint32_t message_digest[5];
|
||||
|
||||
sha.Reset();
|
||||
sha << server_key.c_str();
|
||||
|
||||
if (sha.Result(message_digest)){
|
||||
// convert sha1 hash bytes to network byte order because this sha1
|
||||
// library works on ints rather than bytes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
message_digest[i] = htonl(message_digest[i]);
|
||||
}
|
||||
|
||||
server_key = base64_encode(
|
||||
reinterpret_cast<const unsigned char*>
|
||||
(message_digest),20
|
||||
);
|
||||
|
||||
// set handshake accept headers
|
||||
response.replace_header("Sec-WebSocket-Accept",server_key);
|
||||
response.add_header("Upgrade","websocket");
|
||||
response.add_header("Connection","Upgrade");
|
||||
} else {
|
||||
//m_endpoint->elog().at(log::elevel::RERROR)
|
||||
//<< "Error computing handshake sha1 hash" << log::endl;
|
||||
// TODO: make sure this error path works
|
||||
response.set_status(http::status_code::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void consume(std::istream& s) {
|
||||
while (s.good() && m_state != hybi_state::READY) {
|
||||
try {
|
||||
switch (m_state) {
|
||||
case hybi_state::READ_HEADER:
|
||||
process_header(s);
|
||||
break;
|
||||
case hybi_state::READ_PAYLOAD:
|
||||
process_payload(s);
|
||||
break;
|
||||
case hybi_state::READY:
|
||||
// shouldn't be here..
|
||||
break;
|
||||
case hybi_state::IGNORE:
|
||||
s.ignore(m_payload_left);
|
||||
m_payload_left -= static_cast<size_t>(s.gcount());
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
reset();
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (const processor::exception& e) {
|
||||
if (e.code() != processor::error::OUT_OF_MESSAGES) {
|
||||
// The out of messages exception acts as an inturrupt rather
|
||||
// than an error. In that case we don't want to reset
|
||||
// processor state. In all other cases we are aborting
|
||||
// processing of the message in flight and want to reset the
|
||||
// processor for a new message.
|
||||
if (m_header.ready()) {
|
||||
m_header.reset();
|
||||
ignore();
|
||||
}
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sends the processor an inturrupt signal instructing it to ignore the next
|
||||
// num bytes and then reset itself. This is used to flush a bad frame out of
|
||||
// the read buffer.
|
||||
void ignore() {
|
||||
m_state = hybi_state::IGNORE;
|
||||
}
|
||||
|
||||
void process_header(std::istream& s) {
|
||||
m_header.consume(s);
|
||||
|
||||
if (m_header.ready()) {
|
||||
// Get a free message from the read queue for the type of the
|
||||
// current message
|
||||
if (m_header.is_control()) {
|
||||
process_control_header();
|
||||
} else {
|
||||
process_data_header();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process_control_header() {
|
||||
m_control_message = m_connection.get_control_message();
|
||||
|
||||
if (!m_control_message) {
|
||||
throw processor::exception("Out of control messages",
|
||||
processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_control_message->reset(m_header.get_opcode(),m_header.get_masking_key());
|
||||
|
||||
m_payload_left = static_cast<size_t>(m_header.get_payload_size());
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
process_frame();
|
||||
} else {
|
||||
m_state = hybi_state::READ_PAYLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
void process_data_header() {
|
||||
if (!m_data_message) {
|
||||
// This is a new message. No continuation frames allowed.
|
||||
if (m_header.get_opcode() == frame::opcode::CONTINUATION) {
|
||||
throw processor::exception("Received continuation frame without an outstanding message.",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
m_data_message = m_connection.get_data_message();
|
||||
|
||||
if (!m_data_message) {
|
||||
throw processor::exception("Out of data messages",
|
||||
processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_data_message->reset(m_header.get_opcode());
|
||||
} else {
|
||||
// A message has already been started. Continuation frames only!
|
||||
if (m_header.get_opcode() != frame::opcode::CONTINUATION) {
|
||||
throw processor::exception("Received new message before the completion of the existing one.",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
m_payload_left = static_cast<size_t>(m_header.get_payload_size());
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
process_frame();
|
||||
} else {
|
||||
// each frame has a new masking key
|
||||
m_data_message->set_masking_key(m_header.get_masking_key());
|
||||
m_state = hybi_state::READ_PAYLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
void process_payload(std::istream& input) {
|
||||
//std::cout << "payload left 1: " << m_payload_left << std::endl;
|
||||
size_t num;
|
||||
|
||||
// read bytes into processor buffer. Read the lesser of the buffer size
|
||||
// and the number of bytes left in the payload.
|
||||
|
||||
input.read(m_payload_buffer, std::min(m_payload_left, PAYLOAD_BUFFER_SIZE));
|
||||
num = static_cast<size_t>(input.gcount());
|
||||
|
||||
if (input.bad()) {
|
||||
throw processor::exception("istream readsome error",
|
||||
processor::error::FATAL_ERROR);
|
||||
}
|
||||
|
||||
if (num == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_payload_left -= num;
|
||||
|
||||
// tell the appropriate message to process the bytes.
|
||||
if (m_header.is_control()) {
|
||||
m_control_message->process_payload(m_payload_buffer,num);
|
||||
} else {
|
||||
//m_connection.alog().at(log::alevel::DEVEL) << "process_payload. Size: " << m_payload_left << log::endl;
|
||||
m_data_message->process_payload(m_payload_buffer,num);
|
||||
}
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
process_frame();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void process_frame() {
|
||||
if (m_header.get_fin()) {
|
||||
if (m_header.is_control()) {
|
||||
m_control_message->complete();
|
||||
} else {
|
||||
m_data_message->complete();
|
||||
}
|
||||
m_state = hybi_state::READY;
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool ready() const {
|
||||
return m_state == hybi_state::READY;
|
||||
}
|
||||
|
||||
bool is_control() const {
|
||||
return m_header.is_control();
|
||||
}
|
||||
|
||||
// note this can only be called once
|
||||
message::data_ptr get_data_message() {
|
||||
message::data_ptr p = m_data_message;
|
||||
m_data_message.reset();
|
||||
return p;
|
||||
}
|
||||
|
||||
// note this can only be called once
|
||||
message::control_ptr get_control_message() {
|
||||
message::control_ptr p = m_control_message;
|
||||
m_control_message.reset();
|
||||
return p;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_state = hybi_state::READ_HEADER;
|
||||
m_header.reset();
|
||||
}
|
||||
|
||||
uint64_t get_bytes_needed() const {
|
||||
switch (m_state) {
|
||||
case hybi_state::READ_HEADER:
|
||||
return m_header.get_bytes_needed();
|
||||
case hybi_state::READ_PAYLOAD:
|
||||
case hybi_state::IGNORE:
|
||||
return m_payload_left;
|
||||
case hybi_state::READY:
|
||||
return 0;
|
||||
default:
|
||||
throw "shouldn't be here";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace all this to remove all lingering dependencies on
|
||||
// websocket_frame
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const utf8_string& payload) {
|
||||
/*if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}*/
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
|
||||
|
||||
binary_string_ptr response(new binary_string(0));
|
||||
|
||||
|
||||
m_write_frame.reset();
|
||||
m_write_frame.set_opcode(opcode);
|
||||
m_write_frame.set_masked(mask);
|
||||
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_payload(payload);
|
||||
|
||||
|
||||
m_write_frame.process_payload();
|
||||
|
||||
// TODO
|
||||
response->resize(m_write_frame.get_header_len()+m_write_frame.get_payload().size());
|
||||
|
||||
// copy header
|
||||
std::copy(m_write_frame.get_header(),m_write_frame.get_header()+m_write_frame.get_header_len(),response->begin());
|
||||
|
||||
// copy payload
|
||||
std::copy(m_write_frame.get_payload().begin(),m_write_frame.get_payload().end(),response->begin()+m_write_frame.get_header_len());
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const binary_string& payload) {
|
||||
/*if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}*/
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
binary_string_ptr response(new binary_string(0));
|
||||
|
||||
m_write_frame.reset();
|
||||
m_write_frame.set_opcode(opcode);
|
||||
m_write_frame.set_masked(mask);
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_payload(payload);
|
||||
|
||||
m_write_frame.process_payload();
|
||||
|
||||
// TODO
|
||||
response->resize(m_write_frame.get_header_len()+m_write_frame.get_payload().size());
|
||||
|
||||
// copy header
|
||||
std::copy(m_write_frame.get_header(),m_write_frame.get_header()+m_write_frame.get_header_len(),response->begin());
|
||||
|
||||
// copy payload
|
||||
std::copy(m_write_frame.get_payload().begin(),m_write_frame.get_payload().end(),response->begin()+m_write_frame.get_header_len());
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/*binary_string_ptr prepare_close_frame(close::status::value code,
|
||||
bool mask,
|
||||
const std::string& reason) {
|
||||
binary_string_ptr response(new binary_string(0));
|
||||
|
||||
m_write_frame.reset();
|
||||
m_write_frame.set_opcode(frame::opcode::CLOSE);
|
||||
m_write_frame.set_masked(mask);
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_status(code,reason);
|
||||
|
||||
m_write_frame.process_payload();
|
||||
|
||||
// TODO
|
||||
response->resize(m_write_frame.get_header_len()+m_write_frame.get_payload().size());
|
||||
|
||||
// copy header
|
||||
std::copy(m_write_frame.get_header(),m_write_frame.get_header()+m_write_frame.get_header_len(),response->begin());
|
||||
|
||||
// copy payload
|
||||
std::copy(m_write_frame.get_payload().begin(),m_write_frame.get_payload().end(),response->begin()+m_write_frame.get_header_len());
|
||||
|
||||
return response;
|
||||
}*/
|
||||
|
||||
// new prepare frame stuff
|
||||
void prepare_frame(message::data_ptr msg) {
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg->validate_payload();
|
||||
|
||||
bool masked = !m_connection.is_server();
|
||||
int32_t key = m_connection.rand();
|
||||
|
||||
m_write_header.reset();
|
||||
m_write_header.set_fin(true);
|
||||
m_write_header.set_opcode(msg->get_opcode());
|
||||
m_write_header.set_masked(masked,key);
|
||||
m_write_header.set_payload_size(msg->get_payload().size());
|
||||
m_write_header.complete();
|
||||
|
||||
msg->set_header(m_write_header.get_header_bytes());
|
||||
|
||||
if (masked) {
|
||||
msg->set_masking_key(key);
|
||||
msg->mask();
|
||||
}
|
||||
|
||||
msg->set_prepared(true);
|
||||
}
|
||||
|
||||
void prepare_close_frame(message::data_ptr msg,
|
||||
close::status::value code,
|
||||
const std::string& reason)
|
||||
{
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set close payload
|
||||
if (code != close::status::NO_STATUS) {
|
||||
const uint16_t payload = htons(static_cast<u_short>(code));
|
||||
|
||||
msg->set_payload(std::string(reinterpret_cast<const char*>(&payload), 2));
|
||||
msg->append_payload(reason);
|
||||
}
|
||||
|
||||
// prepare rest of frame
|
||||
prepare_frame(msg);
|
||||
}
|
||||
private:
|
||||
// must be divisible by 8 (some things are hardcoded for 4 and 8 byte word
|
||||
// sizes
|
||||
static const size_t PAYLOAD_BUFFER_SIZE = 512;
|
||||
|
||||
connection_type& m_connection;
|
||||
int m_state;
|
||||
|
||||
message::data_ptr m_data_message;
|
||||
message::control_ptr m_control_message;
|
||||
hybi_header m_header;
|
||||
hybi_header m_write_header;
|
||||
size_t m_payload_left;
|
||||
|
||||
char m_payload_buffer[PAYLOAD_BUFFER_SIZE];
|
||||
|
||||
frame::parser<connection_type> m_write_frame; // TODO: refactor this out
|
||||
};
|
||||
|
||||
template <class connection_type>
|
||||
const size_t hybi<connection_type>::PAYLOAD_BUFFER_SIZE;
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
298
Subtrees/websocket/src/processors/hybi_header.cpp
Normal file
298
Subtrees/websocket/src/processors/hybi_header.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hybi_header.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using websocketpp::processor::hybi_header;
|
||||
|
||||
hybi_header::hybi_header() {
|
||||
reset();
|
||||
}
|
||||
void hybi_header::reset() {
|
||||
memset(m_header, 0x00, MAX_HEADER_LENGTH);
|
||||
m_state = STATE_BASIC_HEADER;
|
||||
m_bytes_needed = BASIC_HEADER_LENGTH;
|
||||
}
|
||||
|
||||
// Writing interface (parse a byte stream)
|
||||
void hybi_header::consume(std::istream& input) {
|
||||
switch (m_state) {
|
||||
case STATE_BASIC_HEADER:
|
||||
input.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],
|
||||
m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= input.gcount();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
process_basic_header();
|
||||
|
||||
validate_basic_header();
|
||||
|
||||
if (m_bytes_needed > 0) {
|
||||
m_state = STATE_EXTENDED_HEADER;
|
||||
} else {
|
||||
process_extended_header();
|
||||
m_state = STATE_READY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_EXTENDED_HEADER:
|
||||
input.read(&m_header[get_header_len()-m_bytes_needed],
|
||||
m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= input.gcount();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
process_extended_header();
|
||||
m_state = STATE_READY;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//std::cout << "header so far: " << zsutil::to_hex(std::string(m_header,MAX_HEADER_LENGTH)) << std::endl;
|
||||
}
|
||||
uint64_t hybi_header::get_bytes_needed() const {
|
||||
return m_bytes_needed;
|
||||
}
|
||||
bool hybi_header::ready() const {
|
||||
return m_state == STATE_READY;
|
||||
}
|
||||
|
||||
// Writing interface (set fields directly)
|
||||
void hybi_header::set_fin(bool fin) {
|
||||
set_header_bit(BPB0_FIN,0,fin);
|
||||
}
|
||||
void hybi_header::set_rsv1(bool b) {
|
||||
set_header_bit(BPB0_RSV1,0,b);
|
||||
}
|
||||
void hybi_header::set_rsv2(bool b) {
|
||||
set_header_bit(BPB0_RSV2,0,b);
|
||||
}
|
||||
void hybi_header::set_rsv3(bool b) {
|
||||
set_header_bit(BPB0_RSV3,0,b);
|
||||
}
|
||||
void hybi_header::set_opcode(websocketpp::frame::opcode::value op) {
|
||||
m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits
|
||||
m_header[0] |= op; // set op bits
|
||||
}
|
||||
void hybi_header::set_masked(bool masked,int32_t key) {
|
||||
if (masked) {
|
||||
m_header[1] |= BPB1_MASK;
|
||||
set_masking_key(key);
|
||||
} else {
|
||||
m_header[1] &= (0xFF ^ BPB1_MASK);
|
||||
clear_masking_key();
|
||||
}
|
||||
}
|
||||
void hybi_header::set_payload_size(uint64_t size) {
|
||||
if (size <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_header[1] |= size;
|
||||
m_payload_size = size;
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
if (get_masked()) {
|
||||
// shift mask bytes to the correct position given the new size
|
||||
unsigned int mask_offset = get_header_len()-4;
|
||||
m_header[1] |= BASIC_PAYLOAD_16BIT_CODE;
|
||||
memcpy(&m_header[get_header_len()-4], &m_header[mask_offset], 4);
|
||||
} else {
|
||||
m_header[1] |= BASIC_PAYLOAD_16BIT_CODE;
|
||||
}
|
||||
m_payload_size = size;
|
||||
*(reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])) = htons(static_cast<uint16_t>(size));
|
||||
|
||||
/* uint16_t net_size = htons(static_cast<uint16_t>(size));
|
||||
//memcpy(&m_header[BASIC_HEADER_LENGTH], &net_size, sizeof(uint16_t));
|
||||
std::copy(
|
||||
reinterpret_cast<char*>(&net_size),
|
||||
reinterpret_cast<char*>(&net_size)+sizeof(uint16_t),
|
||||
&m_header[BASIC_HEADER_LENGTH]
|
||||
);*/
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_JUMBO) {
|
||||
if (get_masked()) {
|
||||
// shift mask bytes to the correct position given the new size
|
||||
unsigned int mask_offset = get_header_len()-4;
|
||||
m_header[1] |= BASIC_PAYLOAD_64BIT_CODE;
|
||||
memcpy(&m_header[get_header_len()-4], &m_header[mask_offset], 4);
|
||||
} else {
|
||||
m_header[1] |= BASIC_PAYLOAD_64BIT_CODE;
|
||||
}
|
||||
m_payload_size = size;
|
||||
*(reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])) = zsutil::htonll(size);
|
||||
} else {
|
||||
throw processor::exception("set_payload_size called with value that was too large (>2^63)",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
|
||||
}
|
||||
void hybi_header::complete() {
|
||||
validate_basic_header();
|
||||
m_state = STATE_READY;
|
||||
}
|
||||
|
||||
// Reading interface (get string of bytes)
|
||||
std::string hybi_header::get_header_bytes() const {
|
||||
return std::string(m_header,get_header_len());
|
||||
}
|
||||
|
||||
// Reading interface (get fields directly)
|
||||
bool hybi_header::get_fin() const {
|
||||
return ((m_header[0] & BPB0_FIN) == BPB0_FIN);
|
||||
}
|
||||
bool hybi_header::get_rsv1() const {
|
||||
return ((m_header[0] & BPB0_RSV1) == BPB0_RSV1);
|
||||
}
|
||||
bool hybi_header::get_rsv2() const {
|
||||
return ((m_header[0] & BPB0_RSV2) == BPB0_RSV2);
|
||||
}
|
||||
bool hybi_header::get_rsv3() const {
|
||||
return ((m_header[0] & BPB0_RSV3) == BPB0_RSV3);
|
||||
}
|
||||
websocketpp::frame::opcode::value hybi_header::get_opcode() const {
|
||||
return frame::opcode::value(m_header[0] & BPB0_OPCODE);
|
||||
}
|
||||
bool hybi_header::get_masked() const {
|
||||
return ((m_header[1] & BPB1_MASK) == BPB1_MASK);
|
||||
}
|
||||
int32_t hybi_header::get_masking_key() const {
|
||||
if (!get_masked()) {
|
||||
return 0;
|
||||
}
|
||||
return *reinterpret_cast<const int32_t*>(&m_header[get_header_len()-4]);
|
||||
}
|
||||
uint64_t hybi_header::get_payload_size() const {
|
||||
return m_payload_size;
|
||||
}
|
||||
|
||||
bool hybi_header::is_control() const {
|
||||
return (frame::opcode::is_control(get_opcode()));
|
||||
}
|
||||
|
||||
// private
|
||||
unsigned int hybi_header::get_header_len() const {
|
||||
unsigned int temp = 2;
|
||||
|
||||
if (get_masked()) {
|
||||
temp += 4;
|
||||
}
|
||||
|
||||
if (get_basic_size() == 126) {
|
||||
temp += 2;
|
||||
} else if (get_basic_size() == 127) {
|
||||
temp += 8;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
uint8_t hybi_header::get_basic_size() const {
|
||||
return m_header[1] & BPB1_PAYLOAD;
|
||||
}
|
||||
|
||||
void hybi_header::validate_basic_header() const {
|
||||
// check for control frame size
|
||||
if (is_control() && get_basic_size() > frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw processor::exception("Control Frame is too large",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved bits
|
||||
if (get_rsv1() || get_rsv2() || get_rsv3()) {
|
||||
throw processor::exception("Reserved bit used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved opcodes
|
||||
if (frame::opcode::reserved(get_opcode())) {
|
||||
throw processor::exception("Reserved opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for invalid opcodes
|
||||
if (frame::opcode::invalid(get_opcode())) {
|
||||
throw processor::exception("Invalid opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for fragmented control message
|
||||
if (is_control() && !get_fin()) {
|
||||
throw processor::exception("Fragmented control message",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::process_basic_header() {
|
||||
m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH;
|
||||
}
|
||||
void hybi_header::process_extended_header() {
|
||||
uint8_t s = get_basic_size();
|
||||
|
||||
if (s <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_payload_size = s;
|
||||
} else if (s == BASIC_PAYLOAD_16BIT_CODE) {
|
||||
// reinterpret the second two bytes as a 16 bit integer in network
|
||||
// byte order. Convert to host byte order and store locally.
|
||||
m_payload_size = ntohs(*(
|
||||
reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size < s) {
|
||||
std::stringstream err;
|
||||
err << "payload length not minimally encoded. Using 16 bit form for payload size: " << m_payload_size;
|
||||
throw processor::exception(err.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else if (s == BASIC_PAYLOAD_64BIT_CODE) {
|
||||
// reinterpret the second eight bytes as a 64 bit integer in
|
||||
// network byte order. Convert to host byte order and store.
|
||||
m_payload_size = zsutil::ntohll(*(
|
||||
reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
throw processor::exception("payload length not minimally encoded",
|
||||
processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: shouldn't be here how to handle?
|
||||
throw processor::exception("invalid get_basic_size in process_extended_header");
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::set_header_bit(uint8_t bit,int byte,bool value) {
|
||||
if (value) {
|
||||
m_header[byte] |= bit;
|
||||
} else {
|
||||
m_header[byte] &= (0xFF ^ bit);
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::set_masking_key(int32_t key) {
|
||||
*(reinterpret_cast<int32_t *>(&m_header[get_header_len()-4])) = key;
|
||||
}
|
||||
void hybi_header::clear_masking_key() {
|
||||
// this is a no-op as clearing the mask bit also changes the get_header_len
|
||||
// method to not include these byte ranges. Whenever the masking bit is re-
|
||||
// set a new key is generated anyways.
|
||||
}
|
||||
147
Subtrees/websocket/src/processors/hybi_header.hpp
Normal file
147
Subtrees/websocket/src/processors/hybi_header.hpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HYBI_HEADER_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_HEADER_HPP
|
||||
|
||||
#include "processor.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
/// Describes a processor for reading and writing WebSocket frame headers
|
||||
/**
|
||||
* The hybi_header class provides a processor capable of reading and writing
|
||||
* WebSocket frame headers. It has two writing modes and two reading modes.
|
||||
*
|
||||
* Writing method 1: call consume() until ready()
|
||||
* Writing method 2: call set_* methods followed by complete()
|
||||
*
|
||||
* Writing methods are valid only when ready() returns false. Use reset() to
|
||||
* reset the header for writing again. Mixing writing methods between calls to
|
||||
* reset() may behave unpredictably.
|
||||
*
|
||||
* Reading method 1: call get_header_bytes() to return a string of bytes
|
||||
* Reading method 2: call get_* methods to read individual values
|
||||
*
|
||||
* Reading methods are valid only when ready() is true.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe
|
||||
*/
|
||||
class hybi_header {
|
||||
public:
|
||||
/// Construct a header processor and initialize for writing
|
||||
hybi_header();
|
||||
/// Reset a header processor for writing
|
||||
void reset();
|
||||
|
||||
// Writing interface (parse a byte stream)
|
||||
// valid only if ready() returns false
|
||||
// Consume will throw a processor::exception in the case that the bytes it
|
||||
// read do not form a valid WebSocket frame header.
|
||||
void consume(std::istream& input);
|
||||
uint64_t get_bytes_needed() const;
|
||||
bool ready() const;
|
||||
|
||||
// Writing interface (set fields directly)
|
||||
// valid only if ready() returns false
|
||||
// set_* may allow invalid values. Call complete() once values are set to
|
||||
// check for header validity.
|
||||
void set_fin(bool fin);
|
||||
void set_rsv1(bool b);
|
||||
void set_rsv2(bool b);
|
||||
void set_rsv3(bool b);
|
||||
void set_opcode(websocketpp::frame::opcode::value op);
|
||||
void set_masked(bool masked,int32_t key);
|
||||
void set_payload_size(uint64_t size);
|
||||
// Complete will throw a processor::exception in the case that the
|
||||
// combination of values set do not form a valid WebSocket frame header.
|
||||
void complete();
|
||||
|
||||
// Reading interface (get string of bytes)
|
||||
// valid only if ready() returns true
|
||||
std::string get_header_bytes() const;
|
||||
|
||||
// Reading interface (get fields directly)
|
||||
// valid only if ready() returns true
|
||||
bool get_fin() const;
|
||||
bool get_rsv1() const;
|
||||
bool get_rsv2() const;
|
||||
bool get_rsv3() const;
|
||||
frame::opcode::value get_opcode() const;
|
||||
bool get_masked() const;
|
||||
// will return zero in the case where get_masked() is false. Note:
|
||||
// a masking key of zero is slightly different than no mask at all.
|
||||
int32_t get_masking_key() const;
|
||||
uint64_t get_payload_size() const;
|
||||
|
||||
bool is_control() const;
|
||||
private:
|
||||
// general helper functions
|
||||
unsigned int get_header_len() const;
|
||||
uint8_t get_basic_size() const;
|
||||
void validate_basic_header() const;
|
||||
|
||||
// helper functions for writing
|
||||
void process_basic_header();
|
||||
void process_extended_header();
|
||||
void set_header_bit(uint8_t bit,int byte,bool value);
|
||||
void set_masking_key(int32_t key);
|
||||
void clear_masking_key();
|
||||
|
||||
// basic payload byte flags
|
||||
static const uint8_t BPB0_OPCODE = 0x0F;
|
||||
static const uint8_t BPB0_RSV3 = 0x10;
|
||||
static const uint8_t BPB0_RSV2 = 0x20;
|
||||
static const uint8_t BPB0_RSV1 = 0x40;
|
||||
static const uint8_t BPB0_FIN = 0x80;
|
||||
static const uint8_t BPB1_PAYLOAD = 0x7F;
|
||||
static const uint8_t BPB1_MASK = 0x80;
|
||||
|
||||
static const uint8_t BASIC_PAYLOAD_16BIT_CODE = 0x7E; // 126
|
||||
static const uint8_t BASIC_PAYLOAD_64BIT_CODE = 0x7F; // 127
|
||||
|
||||
static const unsigned int BASIC_HEADER_LENGTH = 2;
|
||||
static const unsigned int MAX_HEADER_LENGTH = 14;
|
||||
|
||||
static const uint8_t STATE_BASIC_HEADER = 1;
|
||||
static const uint8_t STATE_EXTENDED_HEADER = 2;
|
||||
static const uint8_t STATE_READY = 3;
|
||||
static const uint8_t STATE_WRITE = 4;
|
||||
|
||||
uint8_t m_state;
|
||||
std::streamsize m_bytes_needed;
|
||||
uint64_t m_payload_size;
|
||||
char m_header[MAX_HEADER_LENGTH];
|
||||
};
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HYBI_HEADER_HPP
|
||||
360
Subtrees/websocket/src/processors/hybi_legacy.hpp
Normal file
360
Subtrees/websocket/src/processors/hybi_legacy.hpp
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
|
||||
|
||||
#include "processor.hpp"
|
||||
|
||||
#include "../md5/md5.hpp"
|
||||
#include "../network_utilities.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif // #ifdef min
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
namespace hybi_legacy_state {
|
||||
enum value {
|
||||
INIT = 0,
|
||||
READ = 1,
|
||||
DONE = 2
|
||||
};
|
||||
}
|
||||
|
||||
template <class connection_type>
|
||||
class hybi_legacy : public processor_base {
|
||||
public:
|
||||
hybi_legacy(connection_type &connection)
|
||||
: m_connection(connection),
|
||||
m_state(hybi_legacy_state::INIT)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void validate_handshake(const http::parser::request& /*headers*/) const {
|
||||
|
||||
}
|
||||
|
||||
void handshake_response(const http::parser::request& request,http::parser::response& response) {
|
||||
char key_final[16];
|
||||
|
||||
// copy key1 into final key
|
||||
decode_client_key(request.header("Sec-WebSocket-Key1"), &key_final[0]);
|
||||
|
||||
// copy key2 into final key
|
||||
decode_client_key(request.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.
|
||||
const std::string& key3 = request.header("Sec-WebSocket-Key3");
|
||||
std::copy(key3.c_str(),
|
||||
key3.c_str()+std::min(static_cast<size_t>(8), key3.size()),
|
||||
&key_final[8]);
|
||||
|
||||
m_key3 = md5_hash_string(std::string(key_final,16));
|
||||
|
||||
response.add_header("Upgrade","websocket");
|
||||
response.add_header("Connection","Upgrade");
|
||||
|
||||
// TODO: require headers that need application specific information?
|
||||
|
||||
// Echo back client's origin unless our local application set a
|
||||
// more restrictive one.
|
||||
if (response.header("Sec-WebSocket-Origin") == "") {
|
||||
response.add_header("Sec-WebSocket-Origin",request.header("Origin"));
|
||||
}
|
||||
|
||||
// Echo back the client's request host unless our local application
|
||||
// set a different one.
|
||||
if (response.header("Sec-WebSocket-Location") == "") {
|
||||
// TODO: extract from host header rather than hard code
|
||||
uri_ptr uri = get_uri(request);
|
||||
response.add_header("Sec-WebSocket-Location",uri->str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_origin(const http::parser::request& request) const {
|
||||
return request.header("Origin");
|
||||
}
|
||||
|
||||
uri_ptr get_uri(const http::parser::request& request) const {
|
||||
std::string h = request.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 uri_ptr(new uri(m_connection.is_secure(),h,request.uri()));
|
||||
} else {
|
||||
return uri_ptr(new uri(m_connection.is_secure(),
|
||||
h.substr(0,last_colon),
|
||||
h.substr(last_colon+1),
|
||||
request.uri()));
|
||||
}
|
||||
|
||||
// TODO: check if get_uri is a full uri
|
||||
}
|
||||
|
||||
void consume(std::istream& s) {
|
||||
//unsigned char c;
|
||||
while (s.good() && m_state != hybi_legacy_state::DONE) {
|
||||
//c = s.get();
|
||||
//if (s.good()) {
|
||||
process(s);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
bool ready() const {
|
||||
return m_state == hybi_legacy_state::DONE;
|
||||
}
|
||||
|
||||
// legacy hybi has no control messages.
|
||||
bool is_control() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
message::data_ptr get_data_message() {
|
||||
message::data_ptr p = m_data_message;
|
||||
m_data_message.reset();
|
||||
return p;
|
||||
}
|
||||
|
||||
message::control_ptr get_control_message() {
|
||||
throw "Hybi legacy has no control messages.";
|
||||
}
|
||||
|
||||
void process(std::istream& input) {
|
||||
if (m_state == hybi_legacy_state::INIT) {
|
||||
// we are looking for a 0x00
|
||||
if (input.peek() == 0x00) {
|
||||
// start a message
|
||||
input.ignore();
|
||||
|
||||
m_state = hybi_legacy_state::READ;
|
||||
|
||||
m_data_message = m_connection.get_data_message();
|
||||
|
||||
if (!m_data_message) {
|
||||
throw processor::exception("Out of data messages",processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_data_message->reset(frame::opcode::TEXT);
|
||||
} else {
|
||||
input.ignore();
|
||||
// TODO: ignore or error
|
||||
//std::stringstream foo;
|
||||
|
||||
//foo << "invalid character read: |" << input.peek() << "|";
|
||||
|
||||
std::cout << "invalid character read: |" << input.peek() << "|" << std::endl;
|
||||
|
||||
//throw processor::exception(foo.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
} else if (m_state == hybi_legacy_state::READ) {
|
||||
if (input.peek() == 0xFF) {
|
||||
// end
|
||||
input.ignore();
|
||||
|
||||
m_state = hybi_legacy_state::DONE;
|
||||
} else {
|
||||
if (m_data_message) {
|
||||
size_t num;
|
||||
|
||||
input.get(m_payload_buffer, PAYLOAD_BUFFER_SIZE, '\xFF');
|
||||
|
||||
num = static_cast<size_t>(input.gcount());
|
||||
|
||||
if (input.bad()) {
|
||||
throw processor::exception("istream readsome error",
|
||||
processor::error::FATAL_ERROR);
|
||||
}
|
||||
|
||||
m_data_message->process_payload(m_payload_buffer,num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_state = hybi_legacy_state::INIT;
|
||||
m_data_message.reset();
|
||||
}
|
||||
|
||||
uint64_t get_bytes_needed() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string get_key3() const {
|
||||
return m_key3;
|
||||
}
|
||||
|
||||
// TODO: to factor away
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const binary_string& payload) {
|
||||
if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}
|
||||
|
||||
// TODO: mask = ignore?
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
binary_string_ptr response(new binary_string(payload.size()+2));
|
||||
|
||||
(*response)[0] = 0x00;
|
||||
std::copy(payload.begin(),payload.end(),response->begin()+1);
|
||||
(*response)[response->size()-1] = 0xFF;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const utf8_string& payload) {
|
||||
if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}
|
||||
|
||||
// TODO: mask = ignore?
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
binary_string_ptr response(new binary_string(payload.size()+2));
|
||||
|
||||
(*response)[0] = 0x00;
|
||||
std::copy(payload.begin(),payload.end(),response->begin()+1);
|
||||
(*response)[response->size()-1] = 0xFF;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
binary_string_ptr prepare_close_frame(close::status::value code,
|
||||
bool mask,
|
||||
const std::string& reason) {
|
||||
binary_string_ptr response(new binary_string(2));
|
||||
|
||||
(*response)[0] = 0xFF;
|
||||
(*response)[1] = 0x00;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void prepare_frame(message::data_ptr msg) {
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg->set_header(std::string(1,0x00));
|
||||
|
||||
// VFALCO: TODO fix this warning C4309 "truncation of constant value"
|
||||
msg->append_payload(std::string(1,0xFF));
|
||||
|
||||
msg->set_prepared(true);
|
||||
}
|
||||
|
||||
void prepare_close_frame(message::data_ptr msg,
|
||||
close::status::value /*code*/,
|
||||
const std::string& /*reason*/)
|
||||
{
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg->set_header(std::string());
|
||||
|
||||
std::string val;
|
||||
val.append(1,0xFF);
|
||||
val.append(1,0x00);
|
||||
msg->set_payload(val);
|
||||
|
||||
msg->set_prepared(true);
|
||||
}
|
||||
|
||||
private:
|
||||
void decode_client_key(const std::string& key, char* result) {
|
||||
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 = atoi(digits.c_str());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// must be divisible by 8 (some things are hardcoded for 4 and 8 byte word
|
||||
// sizes
|
||||
static const size_t PAYLOAD_BUFFER_SIZE = 512;
|
||||
|
||||
connection_type& m_connection;
|
||||
hybi_legacy_state::value m_state;
|
||||
|
||||
message::data_ptr m_data_message;
|
||||
|
||||
std::string m_key3;
|
||||
|
||||
char m_payload_buffer[PAYLOAD_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
} // processor
|
||||
} // websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
|
||||
65
Subtrees/websocket/src/processors/hybi_util.cpp
Normal file
65
Subtrees/websocket/src/processors/hybi_util.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hybi_util.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
namespace hybi_util {
|
||||
|
||||
size_t prepare_masking_key(const masking_key_type& key) {
|
||||
size_t prepared_key = key.i;
|
||||
size_t wordSize = sizeof(size_t);
|
||||
if (wordSize == 8) {
|
||||
prepared_key <<= 32;
|
||||
prepared_key |= (static_cast<size_t>(key.i) & 0x00000000FFFFFFFFLL);
|
||||
}
|
||||
return prepared_key;
|
||||
}
|
||||
|
||||
size_t circshift_prepared_key(size_t prepared_key, size_t offset) {
|
||||
size_t temp = prepared_key << (sizeof(size_t)-offset)*8;
|
||||
return (prepared_key >> offset*8) | temp;
|
||||
}
|
||||
|
||||
void word_mask_exact(char* data,size_t length,const masking_key_type& key) {
|
||||
size_t prepared_key = prepare_masking_key(key);
|
||||
size_t n = length/sizeof(size_t);
|
||||
size_t* word_data = reinterpret_cast<size_t*>(data);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
word_data[i] ^= prepared_key;
|
||||
}
|
||||
|
||||
for (size_t i = n*sizeof(size_t); i < length; i++) {
|
||||
data[i] ^= key.c[i%4];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace hybi_util
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
70
Subtrees/websocket/src/processors/hybi_util.hpp
Normal file
70
Subtrees/websocket/src/processors/hybi_util.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_HYBI_UTIL_HPP
|
||||
#define WEBSOCKET_HYBI_UTIL_HPP
|
||||
|
||||
#include "../common.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
namespace hybi_util {
|
||||
|
||||
// type used to store a masking key
|
||||
union masking_key_type {
|
||||
int32_t i;
|
||||
char c[4];
|
||||
};
|
||||
|
||||
// extract a masking key into a value the size of a machine word. Machine word
|
||||
// size must be 4 or 8
|
||||
size_t prepare_masking_key(const masking_key_type& key);
|
||||
|
||||
// circularly shifts the supplied prepared masking key by offset bytes
|
||||
// prepared_key must be the output of prepare_masking_key with the associated
|
||||
// restrictions on the machine word size.
|
||||
// offset must be 0, 1, 2, or 3
|
||||
size_t circshift_prepared_key(size_t prepared_key, size_t offset);
|
||||
|
||||
// basic byte by byte mask
|
||||
template <typename iter_type>
|
||||
void byte_mask(iter_type b, iter_type e, const masking_key_type& key, size_t key_offset = 0) {
|
||||
size_t key_index = key_offset;
|
||||
for (iter_type i = b; i != e; i++) {
|
||||
*i ^= key.c[key_index++];
|
||||
key_index %= 4;
|
||||
}
|
||||
}
|
||||
|
||||
// exactly masks the bytes from start to end using key `key`
|
||||
void word_mask_exact(char* data,size_t length,const masking_key_type& key);
|
||||
|
||||
} // namespace hybi_util
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_HYBI_UTIL_HPP
|
||||
148
Subtrees/websocket/src/processors/processor.hpp
Normal file
148
Subtrees/websocket/src/processors/processor.hpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HPP
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
namespace error {
|
||||
enum value {
|
||||
FATAL_ERROR = 0, // force session end
|
||||
SOFT_ERROR = 1, // should log and ignore
|
||||
PROTOCOL_VIOLATION = 2, // must end session
|
||||
PAYLOAD_VIOLATION = 3, // should end session
|
||||
INTERNAL_ENDPOINT_ERROR = 4,// cleanly end session
|
||||
MESSAGE_TOO_BIG = 5, // ???
|
||||
OUT_OF_MESSAGES = 6 // read queue is empty, wait
|
||||
};
|
||||
}
|
||||
|
||||
class exception : public std::exception {
|
||||
public:
|
||||
exception(const std::string& msg,
|
||||
error::value code = error::FATAL_ERROR)
|
||||
: m_msg(msg),m_code(code) {}
|
||||
~exception() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
error::value code() const throw() {
|
||||
return m_code;
|
||||
}
|
||||
|
||||
std::string m_msg;
|
||||
error::value m_code;
|
||||
};
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#include "../http/parser.hpp"
|
||||
#include "../uri.hpp"
|
||||
#include "../websocket_frame.hpp" // TODO: clean up
|
||||
|
||||
#include "../messages/data.hpp"
|
||||
#include "../messages/control.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
class processor_base : boost::noncopyable {
|
||||
public:
|
||||
virtual ~processor_base() {}
|
||||
// validate client handshake
|
||||
// validate server handshake
|
||||
|
||||
// Given a list of HTTP headers determine if the values are sufficient
|
||||
// to start a websocket session. If so begin constructing a response, if not throw a handshake
|
||||
// exception.
|
||||
// validate handshake request
|
||||
virtual void validate_handshake(const http::parser::request& headers) const = 0;
|
||||
|
||||
virtual void handshake_response(const http::parser::request& request,http::parser::response& response) = 0;
|
||||
|
||||
// Extracts client origin from a handshake request
|
||||
virtual std::string get_origin(const http::parser::request& request) const = 0;
|
||||
// Extracts client uri from a handshake request
|
||||
virtual uri_ptr get_uri(const http::parser::request& request) const = 0;
|
||||
|
||||
// consume bytes, throw on exception
|
||||
virtual void consume(std::istream& s) = 0;
|
||||
|
||||
// is there a message ready to be dispatched?
|
||||
virtual bool ready() const = 0;
|
||||
virtual bool is_control() const = 0;
|
||||
virtual message::data_ptr get_data_message() = 0;
|
||||
virtual message::control_ptr get_control_message() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual uint64_t get_bytes_needed() const = 0;
|
||||
|
||||
// Get information about the message that is ready
|
||||
//virtual frame::opcode::value get_opcode() const = 0;
|
||||
|
||||
//virtual utf8_string_ptr get_utf8_payload() const = 0;
|
||||
//virtual binary_string_ptr get_binary_payload() const = 0;
|
||||
//virtual close::status::value get_close_code() const = 0;
|
||||
//virtual utf8_string get_close_reason() const = 0;
|
||||
|
||||
// TODO: prepare a frame
|
||||
//virtual binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
// bool mask,
|
||||
// const utf8_string& payload) = 0;
|
||||
//virtual binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
// bool mask,
|
||||
// const binary_string& payload) = 0;
|
||||
//
|
||||
//virtual binary_string_ptr prepare_close_frame(close::status::value code,
|
||||
// bool mask,
|
||||
// const std::string& reason) = 0;
|
||||
|
||||
virtual void prepare_frame(message::data_ptr msg) = 0;
|
||||
virtual void prepare_close_frame(message::data_ptr msg,
|
||||
close::status::value code,
|
||||
const std::string& reason) = 0;
|
||||
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<processor_base> ptr;
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HPP
|
||||
Reference in New Issue
Block a user