Merge commit '09987d0f9d32e860f1391bb9c75b799501e2d141' as 'Subtrees/websocket'

This commit is contained in:
Vinnie Falco
2013-06-20 17:15:11 -07:00
255 changed files with 50217 additions and 0 deletions

View 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

View 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.
}

View 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

View 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

View 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

View 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

View 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