mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
merges all of the policy-refactor changes into one library. Policy refactor branch now passes all autobahn server tests except a few edge close behavior cases.
This commit is contained in:
@@ -30,9 +30,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// for exceptions that should be somewhere else
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
@@ -52,34 +50,19 @@ namespace websocketpp {
|
||||
|
||||
inline uint16_t default_port(bool secure) {
|
||||
return (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT);
|
||||
}
|
||||
|
||||
namespace session {
|
||||
namespace state {
|
||||
enum value {
|
||||
CONNECTING = 0,
|
||||
OPEN = 1,
|
||||
CLOSING = 2,
|
||||
CLOSED = 3
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// System logging levels
|
||||
static const uint16_t LOG_ALL = 0;
|
||||
static const uint16_t LOG_DEBUG = 1;
|
||||
static const uint16_t LOG_INFO = 2;
|
||||
static const uint16_t LOG_WARN = 3;
|
||||
static const uint16_t LOG_ERROR = 4;
|
||||
static const uint16_t LOG_FATAL = 5;
|
||||
static const uint16_t LOG_OFF = 6;
|
||||
|
||||
// Access logging controls
|
||||
// Individual bits
|
||||
static const uint16_t ALOG_CONNECT = 0x1;
|
||||
static const uint16_t ALOG_DISCONNECT = 0x2;
|
||||
static const uint16_t ALOG_MISC_CONTROL = 0x4;
|
||||
static const uint16_t ALOG_FRAME = 0x8;
|
||||
static const uint16_t ALOG_MESSAGE = 0x10;
|
||||
static const uint16_t ALOG_INFO = 0x20;
|
||||
static const uint16_t ALOG_HANDSHAKE = 0x40;
|
||||
// Useful groups
|
||||
static const uint16_t ALOG_OFF = 0x0;
|
||||
static const uint16_t ALOG_CONTROL = ALOG_CONNECT
|
||||
& ALOG_DISCONNECT
|
||||
& ALOG_MISC_CONTROL;
|
||||
static const uint16_t ALOG_ALL = 0xFFFF;
|
||||
|
||||
|
||||
namespace close {
|
||||
namespace status {
|
||||
enum value {
|
||||
@@ -115,76 +98,6 @@ namespace websocketpp {
|
||||
}
|
||||
}
|
||||
|
||||
namespace frame {
|
||||
namespace error {
|
||||
enum value {
|
||||
FATAL_SESSION_ERROR = 0, // force session end
|
||||
SOFT_SESSION_ERROR = 1, // should log and ignore
|
||||
PROTOCOL_VIOLATION = 2, // must end session
|
||||
PAYLOAD_VIOLATION = 3, // should end session
|
||||
INTERNAL_SERVER_ERROR = 4, // cleanly end session
|
||||
MESSAGE_TOO_BIG = 5 // ???
|
||||
};
|
||||
}
|
||||
|
||||
// Opcodes are 4 bits
|
||||
// See spec section 5.2
|
||||
namespace opcode {
|
||||
enum value {
|
||||
CONTINUATION = 0x0,
|
||||
TEXT = 0x1,
|
||||
BINARY = 0x2,
|
||||
RSV3 = 0x3,
|
||||
RSV4 = 0x4,
|
||||
RSV5 = 0x5,
|
||||
RSV6 = 0x6,
|
||||
RSV7 = 0x7,
|
||||
CLOSE = 0x8,
|
||||
PING = 0x9,
|
||||
PONG = 0xA,
|
||||
CONTROL_RSVB = 0xB,
|
||||
CONTROL_RSVC = 0xC,
|
||||
CONTROL_RSVD = 0xD,
|
||||
CONTROL_RSVE = 0xE,
|
||||
CONTROL_RSVF = 0xF,
|
||||
};
|
||||
|
||||
inline bool reserved(value v) {
|
||||
return (v >= RSV3 && v <= RSV7) ||
|
||||
(v >= CONTROL_RSVB && v <= CONTROL_RSVF);
|
||||
}
|
||||
|
||||
inline bool invalid(value v) {
|
||||
return (v > 0xF || v < 0);
|
||||
}
|
||||
|
||||
inline bool is_control(value v) {
|
||||
return v >= 0x8;
|
||||
}
|
||||
}
|
||||
|
||||
namespace limits {
|
||||
static const uint8_t PAYLOAD_SIZE_BASIC = 125;
|
||||
static const uint16_t PAYLOAD_SIZE_EXTENDED = 0xFFFF; // 2^16, 65535
|
||||
static const uint64_t PAYLOAD_SIZE_JUMBO = 0x7FFFFFFFFFFFFFFF;//2^63
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: these classes need a better place to live
|
||||
class server_error : public std::exception {
|
||||
public:
|
||||
server_error(const std::string& msg)
|
||||
: m_msg(msg) {}
|
||||
~server_error() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
private:
|
||||
std::string m_msg;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_CONSTANTS_HPP
|
||||
@@ -28,12 +28,14 @@
|
||||
#ifndef WEBSOCKETPP_CONNECTION_HPP
|
||||
#define WEBSOCKETPP_CONNECTION_HPP
|
||||
|
||||
//#include "endpoint.hpp"
|
||||
#include "common.hpp"
|
||||
#include "http/parser.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
#include "roles/server.hpp"
|
||||
|
||||
#include "processors/hybi.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
@@ -42,62 +44,9 @@
|
||||
#include <iostream> // temporary?
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
// types to use for utf8 string and binary messages
|
||||
typedef std::vector<unsigned char> binary_string;
|
||||
typedef boost::shared_ptr<binary_string> binary_string_ptr;
|
||||
|
||||
typedef std::string utf8_string;
|
||||
typedef boost::shared_ptr<utf8_string> utf8_string_ptr;
|
||||
|
||||
// Universal close status codes. Might be better somewhere else.
|
||||
namespace close {
|
||||
namespace status {
|
||||
enum value {
|
||||
INVALID_END = 999,
|
||||
NORMAL = 1000,
|
||||
GOING_AWAY = 1001,
|
||||
PROTOCOL_ERROR = 1002,
|
||||
UNSUPPORTED_DATA = 1003,
|
||||
RSV_ADHOC_1 = 1004,
|
||||
NO_STATUS = 1005,
|
||||
ABNORMAL_CLOSE = 1006,
|
||||
INVALID_PAYLOAD = 1007,
|
||||
POLICY_VIOLATION = 1008,
|
||||
MESSAGE_TOO_BIG = 1009,
|
||||
EXTENSION_REQUIRE = 1010,
|
||||
RSV_START = 1011,
|
||||
RSV_END = 2999,
|
||||
INVALID_START = 5000
|
||||
};
|
||||
|
||||
inline bool reserved(value s) {
|
||||
return ((s >= RSV_START && s <= RSV_END) ||
|
||||
s == RSV_ADHOC_1);
|
||||
}
|
||||
|
||||
inline bool invalid(value s) {
|
||||
return ((s <= INVALID_END || s >= INVALID_START) ||
|
||||
s == NO_STATUS ||
|
||||
s == ABNORMAL_CLOSE);
|
||||
}
|
||||
|
||||
// TODO functions for application ranges?
|
||||
}
|
||||
}
|
||||
|
||||
namespace session {
|
||||
namespace state {
|
||||
enum value {
|
||||
CONNECTING = 0,
|
||||
OPEN = 1,
|
||||
CLOSING = 2,
|
||||
CLOSED = 3
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct connection_traits;
|
||||
@@ -124,14 +73,28 @@ public:
|
||||
// friends (would require C++11) this would enable connection::start to be
|
||||
// protected instead of public.
|
||||
// friend typename endpoint_traits<endpoint_type>::role_type;
|
||||
//friend class role<endpoint>;
|
||||
//friend class socket<endpoint>;
|
||||
|
||||
friend class role<endpoint>:: template connection<type>;
|
||||
friend class socket<endpoint>:: template connection<type>;
|
||||
|
||||
enum write_state {
|
||||
IDLE = 0,
|
||||
WRITING = 1,
|
||||
INTURRUPT = 2
|
||||
};
|
||||
|
||||
connection(endpoint_type& e)
|
||||
: role_type(e),
|
||||
socket_type(e),
|
||||
m_endpoint(e) {}
|
||||
m_endpoint(e),
|
||||
m_timer(e.endpoint_base::m_io_service,boost::posix_time::seconds(0)),
|
||||
m_state(session::state::CONNECTING),
|
||||
m_write_buffer(0),
|
||||
m_write_state(IDLE) {}
|
||||
|
||||
// SHOULD BE PROTECTED
|
||||
void start() {
|
||||
// initialize the socket.
|
||||
socket_type::async_init(
|
||||
@@ -142,6 +105,7 @@ public:
|
||||
)
|
||||
);
|
||||
}
|
||||
// END PROTECTED
|
||||
|
||||
// Valid always
|
||||
session::state::value get_state() const {
|
||||
@@ -149,62 +113,155 @@ public:
|
||||
}
|
||||
|
||||
// Valid for OPEN state
|
||||
void send(utf8_string_ptr msg) {}
|
||||
void send(binary_string_ptr data) {}
|
||||
void close(close::status::value code, const utf8_string& reason) {}
|
||||
void ping(binary_string_ptr data) {}
|
||||
void pong(binary_string_ptr data) {}
|
||||
void send(const utf8_string& payload) {
|
||||
binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::TEXT,
|
||||
false,payload));
|
||||
|
||||
m_endpoint.endpoint_base::m_io_service.post(
|
||||
boost::bind(
|
||||
&type::write_message,
|
||||
type::shared_from_this(),
|
||||
msg));
|
||||
}
|
||||
void send(const binary_string& data) {
|
||||
binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::BINARY,
|
||||
false,data));
|
||||
m_endpoint.endpoint_base::m_io_service.post(
|
||||
boost::bind(
|
||||
&type::write_message,
|
||||
type::shared_from_this(),
|
||||
msg));
|
||||
}
|
||||
void close(close::status::value code, const utf8_string& reason) {
|
||||
// TODO:
|
||||
}
|
||||
void ping(const binary_string& payload) {
|
||||
binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::PING,
|
||||
false,payload));
|
||||
|
||||
m_endpoint.m_io_service.post(
|
||||
boost::bind(
|
||||
&type::write_message,
|
||||
type::shared_from_this(),
|
||||
msg));
|
||||
}
|
||||
void pong(const binary_string& payload) {
|
||||
binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::PONG,
|
||||
false,payload));
|
||||
m_endpoint.m_io_service.post(
|
||||
boost::bind(
|
||||
&type::write_message,
|
||||
type::shared_from_this(),
|
||||
msg));
|
||||
}
|
||||
|
||||
uint64_t buffered_amount() const;
|
||||
uint64_t buffered_amount() const {
|
||||
return m_write_buffer;
|
||||
}
|
||||
|
||||
// Valid for CLOSED state
|
||||
close::status::value get_local_close_code() const {};
|
||||
utf8_string get_local_close_reason() const {};
|
||||
close::status::value get_remote_close_code() const {};
|
||||
utf8_string get_remote_close_reason() const {};
|
||||
bool failed_by_me() const {};
|
||||
bool dropped_by_me() const {};
|
||||
bool closed_by_me() const {};
|
||||
close::status::value get_local_close_code() const {
|
||||
return m_local_close_code;
|
||||
}
|
||||
utf8_string get_local_close_reason() const {
|
||||
return m_local_close_reason;
|
||||
}
|
||||
close::status::value get_remote_close_code() const {
|
||||
return m_remote_close_code;
|
||||
}
|
||||
utf8_string get_remote_close_reason() const {
|
||||
return m_remote_close_reason;
|
||||
}
|
||||
bool get_failed_by_me() const {
|
||||
return m_failed_by_me;
|
||||
}
|
||||
bool get_dropped_by_me() const {
|
||||
return m_dropped_by_me;
|
||||
}
|
||||
bool get_closed_by_me() const {
|
||||
return m_closed_by_me;
|
||||
}
|
||||
protected:
|
||||
void handle_socket_init(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
m_endpoint.elog().at(log::elevel::ERROR) << "Connection initialization failed, error code: " << error << log::endl;
|
||||
this->terminate();
|
||||
this->terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this->websocket_handshake();
|
||||
role_type::async_init();
|
||||
}
|
||||
|
||||
void websocket_handshake() {
|
||||
m_endpoint.alog().at(log::alevel::DEVEL) << "Websocket Handshake" << log::endl;
|
||||
void handle_read_frame(const boost::system::error_code& error) {
|
||||
// check if state changed while we were waiting for a read.
|
||||
if (m_state == session::state::CLOSED) { return; }
|
||||
|
||||
socket_type::get_socket().async_read_some(
|
||||
boost::asio::buffer(m_data, 512),
|
||||
boost::bind(
|
||||
&type::handle_read,
|
||||
type::shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_read(const boost::system::error_code& error,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
if (error) {
|
||||
std::cout << "read error" << std::endl;
|
||||
} else {
|
||||
std::cout << "read complete: " << m_data << std::endl;
|
||||
if (error == boost::asio::error::eof) {
|
||||
// got unexpected EOF
|
||||
// TODO: log error
|
||||
terminate(false);
|
||||
} else if (error == boost::asio::error::operation_aborted) {
|
||||
// got unexpected abort (likely our server issued an abort on
|
||||
// all connections on this io_service)
|
||||
|
||||
// TODO: log error
|
||||
terminate(true);
|
||||
} else {
|
||||
// Other unexpected error
|
||||
|
||||
// TODO: log error
|
||||
terminate(false);
|
||||
}
|
||||
}
|
||||
|
||||
// process data from the buffer just read into
|
||||
std::istream s(&m_buf);
|
||||
|
||||
while (m_state != session::state::CLOSED && m_buf.size() > 0) {
|
||||
try {
|
||||
m_processor->consume(s);
|
||||
|
||||
if (m_processor->ready()) {
|
||||
process_message();
|
||||
m_processor->reset();
|
||||
}
|
||||
} catch (const processor::exception& e) {
|
||||
if (m_processor->ready()) {
|
||||
m_processor->reset();
|
||||
}
|
||||
|
||||
if (e.code() == processor::error::PROTOCOL_VIOLATION) {
|
||||
send_close(close::status::PROTOCOL_ERROR, e.what());
|
||||
} else if (e.code() == processor::error::PAYLOAD_VIOLATION) {
|
||||
send_close(close::status::INVALID_PAYLOAD, e.what());
|
||||
} else if (e.code() == processor::error::INTERNAL_SERVER_ERROR) {
|
||||
send_close(close::status::POLICY_VIOLATION, e.what());
|
||||
} else if (e.code() == processor::error::SOFT_ERROR) {
|
||||
// ignore and continue processing frames
|
||||
continue;
|
||||
} else {
|
||||
// Fatal error, forcibly end connection immediately.
|
||||
m_endpoint.elog().at(log::elevel::DEVEL)
|
||||
<< "Dropping TCP due to unrecoverable exception"
|
||||
<< log::endl;
|
||||
terminate(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// try and read more
|
||||
if (m_state != session::state::CLOSED &&
|
||||
m_processor->get_bytes_needed() > 0) {
|
||||
// TODO: read timeout timer?
|
||||
|
||||
std::string r = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nbar";
|
||||
|
||||
boost::asio::async_write(
|
||||
boost::asio::async_read(
|
||||
socket_type::get_socket(),
|
||||
boost::asio::buffer(r, r.size()),
|
||||
m_buf,
|
||||
boost::asio::transfer_at_least(m_processor->get_bytes_needed()),
|
||||
boost::bind(
|
||||
&type::handle_write,
|
||||
&type::handle_read_frame,
|
||||
type::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
@@ -212,29 +269,325 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void process_message() {
|
||||
bool response;
|
||||
switch (m_processor->get_opcode()) {
|
||||
case frame::opcode::TEXT:
|
||||
m_endpoint.get_handler()->on_message(
|
||||
type::shared_from_this(),
|
||||
m_processor->get_utf8_payload());
|
||||
break;
|
||||
case frame::opcode::BINARY:
|
||||
m_endpoint.get_handler()->on_message(
|
||||
type::shared_from_this(),
|
||||
m_processor->get_binary_payload());
|
||||
break;
|
||||
case frame::opcode::PING:
|
||||
response = m_endpoint.get_handler()->on_ping(
|
||||
type::shared_from_this(),
|
||||
m_processor->get_binary_payload());
|
||||
|
||||
if (response) {
|
||||
// send response ping
|
||||
write_message(m_processor->prepare_frame(frame::opcode::PONG,false,*m_processor->get_binary_payload()));
|
||||
}
|
||||
break;
|
||||
case frame::opcode::PONG:
|
||||
m_endpoint.get_handler()->on_pong(
|
||||
type::shared_from_this(),
|
||||
m_processor->get_binary_payload());
|
||||
|
||||
// TODO: disable ping response timer
|
||||
|
||||
break;
|
||||
case frame::opcode::CLOSE:
|
||||
m_remote_close_code = m_processor->get_close_code();
|
||||
m_remote_close_reason = m_processor->get_close_reason();
|
||||
|
||||
// check that the codes we got over the wire are valid
|
||||
|
||||
if (close::status::invalid(m_remote_close_code)) {
|
||||
throw processor::exception("Invalid close code",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (close::status::reserved(m_remote_close_code)) {
|
||||
throw processor::exception("Reserved close code",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (m_state == session::state::OPEN) {
|
||||
// other end is initiating
|
||||
m_endpoint.elog().at(log::elevel::DEVEL)
|
||||
<< "sending close ack" << log::endl;
|
||||
|
||||
// TODO:
|
||||
send_close_ack();
|
||||
} else if (m_state == session::state::CLOSING) {
|
||||
// ack of our close
|
||||
m_endpoint.elog().at(log::elevel::DEVEL)
|
||||
<< "got close ack" << log::endl;
|
||||
|
||||
terminate(false);
|
||||
// TODO: start terminate timer (if client)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw processor::exception("Invalid Opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void send_close(close::status::value code, const std::string& reason) {
|
||||
if (m_state != session::state::OPEN) {
|
||||
m_endpoint.elog().at(log::elevel::WARN)
|
||||
<< "Tried to disconnect a session that wasn't open" << log::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (close::status::invalid(code)) {
|
||||
m_endpoint.elog().at(log::elevel::WARN)
|
||||
<< "Tried to close a connection with invalid close code: " << code << log::endl;
|
||||
return;
|
||||
} else if (close::status::reserved(code)) {
|
||||
m_endpoint.elog().at(log::elevel::WARN)
|
||||
<< "Tried to close a connection with reserved close code: " << code << log::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_state = session::state::CLOSING;
|
||||
|
||||
m_closed_by_me = true;
|
||||
|
||||
m_timer.expires_from_now(boost::posix_time::milliseconds(1000));
|
||||
m_timer.async_wait(
|
||||
boost::bind(
|
||||
&type::fail_on_expire,
|
||||
type::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
|
||||
m_local_close_code = code;
|
||||
m_local_close_reason = reason;
|
||||
|
||||
|
||||
write_message(m_processor->prepare_close_frame(m_local_close_code,
|
||||
false,
|
||||
m_local_close_reason));
|
||||
m_write_state = INTURRUPT;
|
||||
}
|
||||
|
||||
// send an acknowledgement close frame
|
||||
void send_close_ack() {
|
||||
// TODO: state should be OPEN
|
||||
|
||||
// echo close value unless there is a good reason not to.
|
||||
if (m_remote_close_code == close::status::NO_STATUS) {
|
||||
m_local_close_code = close::status::NORMAL;
|
||||
m_local_close_reason = "";
|
||||
} else if (m_remote_close_code == close::status::ABNORMAL_CLOSE) {
|
||||
// TODO: can we possibly get here? This means send_close_ack was
|
||||
// called after a connection ended without getting a close
|
||||
// frame
|
||||
throw "shouldn't be here";
|
||||
} else if (close::status::invalid(m_remote_close_code)) {
|
||||
m_local_close_code = close::status::PROTOCOL_ERROR;
|
||||
m_local_close_reason = "Status code is invalid";
|
||||
} else if (close::status::reserved(m_remote_close_code)) {
|
||||
m_local_close_code = close::status::PROTOCOL_ERROR;
|
||||
m_local_close_reason = "Status code is reserved";
|
||||
} else {
|
||||
m_local_close_code = m_remote_close_code;
|
||||
m_local_close_reason = m_remote_close_reason;
|
||||
}
|
||||
|
||||
// TODO: check whether we should cancel the current in flight write.
|
||||
// if not canceled the close message will be sent as soon as the
|
||||
// current write completes.
|
||||
|
||||
|
||||
write_message(m_processor->prepare_close_frame(m_local_close_code,
|
||||
false,
|
||||
m_local_close_reason));
|
||||
m_write_state = INTURRUPT;
|
||||
}
|
||||
|
||||
void write_message(binary_string_ptr msg) {
|
||||
m_write_buffer += msg->size();
|
||||
m_write_queue.push(msg);
|
||||
write();
|
||||
}
|
||||
|
||||
void write() {
|
||||
switch (m_write_state) {
|
||||
case IDLE:
|
||||
break;
|
||||
case WRITING:
|
||||
// already writing. write() will get called again by the write
|
||||
// handler once it is ready.
|
||||
return;
|
||||
case INTURRUPT:
|
||||
// clear the queue except for the last message
|
||||
while (m_write_queue.size() > 1) {
|
||||
m_write_buffer -= m_write_queue.front()->size();
|
||||
m_write_queue.pop();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// TODO: assert shouldn't be here
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_write_queue.size() > 0) {
|
||||
if (m_write_state == IDLE) {
|
||||
m_write_state = WRITING;
|
||||
}
|
||||
|
||||
boost::asio::async_write(
|
||||
socket_type::get_socket(),
|
||||
boost::asio::buffer(*m_write_queue.front()),
|
||||
boost::bind(
|
||||
&type::handle_write,
|
||||
type::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// if we are in an inturrupted state and had nothing else to write
|
||||
// it is safe to terminate the connection.
|
||||
if (m_write_state == INTURRUPT) {
|
||||
terminate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
std::cout << "write error" << std::endl;
|
||||
} else {
|
||||
std::cout << "write successful" << std::endl;
|
||||
// end session
|
||||
this->terminate();
|
||||
if (error == boost::asio::error::operation_aborted) {
|
||||
// previous write was aborted
|
||||
std::cout << "aborted" << std::endl;
|
||||
} else {
|
||||
log_error("Error writing frame data",error);
|
||||
terminate(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_write_queue.size() == 0) {
|
||||
std::cout << "handle_write called with empty queue" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_write_buffer -= m_write_queue.front()->size();
|
||||
m_write_queue.pop();
|
||||
|
||||
if (m_write_state == WRITING) {
|
||||
m_write_state = IDLE;
|
||||
}
|
||||
|
||||
write();
|
||||
}
|
||||
|
||||
// terminate cleans up a connection and removes it from the endpoint's
|
||||
// connection list.
|
||||
void terminate() {
|
||||
// TODO: close socket cleanly
|
||||
void terminate(bool failed_by_me) {
|
||||
m_endpoint.alog().at(log::alevel::DEBUG_CLOSE) << "terminate called" << log::endl;
|
||||
|
||||
if (m_state == session::state::CLOSED) {
|
||||
// shouldn't be here
|
||||
}
|
||||
|
||||
// cancel the close timeout
|
||||
m_timer.cancel();
|
||||
|
||||
// TODO: figure out why this is really slow
|
||||
m_dropped_by_me = socket_type::shutdown();
|
||||
|
||||
m_failed_by_me = failed_by_me;
|
||||
|
||||
session::state::value old_state = m_state;
|
||||
m_state = session::state::CLOSED;
|
||||
|
||||
// If this was a websocket connection notify the application handler
|
||||
// about the close using either on_fail or on_close
|
||||
if (role_type::get_version() != -1) {
|
||||
if (old_state == session::state::CONNECTING) {
|
||||
m_endpoint.get_handler()->on_fail(type::shared_from_this());
|
||||
} else if (old_state == session::state::OPEN ||
|
||||
old_state == session::state::CLOSING) {
|
||||
m_endpoint.get_handler()->on_close(type::shared_from_this());
|
||||
} else {
|
||||
// if we were already closed something is wrong
|
||||
}
|
||||
log_close_result();
|
||||
}
|
||||
// finally remove this connection from the endpoint's list. This will
|
||||
// remove the last shared pointer to the connection held by WS++.
|
||||
m_endpoint.remove_connection(type::shared_from_this());
|
||||
}
|
||||
|
||||
// this is called when an async asio call encounters an error
|
||||
void log_error(std::string msg,const boost::system::error_code& e) {
|
||||
m_endpoint.elog().at(log::elevel::ERROR)
|
||||
<< msg << "(" << e << ")" << log::endl;
|
||||
}
|
||||
|
||||
void log_close_result() {
|
||||
m_endpoint.alog().at(log::alevel::DISCONNECT)
|
||||
//<< "Disconnect " << (m_was_clean ? "Clean" : "Unclean")
|
||||
<< "Disconnect "
|
||||
<< " close local:[" << m_local_close_code
|
||||
<< (m_local_close_reason == "" ? "" : ","+m_local_close_reason)
|
||||
<< "] remote:[" << m_remote_close_code
|
||||
<< (m_remote_close_reason == "" ? "" : ","+m_remote_close_reason) << "]"
|
||||
<< log::endl;
|
||||
}
|
||||
|
||||
void fail_on_expire(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
if (error != boost::asio::error::operation_aborted) {
|
||||
m_endpoint.elog().at(log::elevel::DEVEL)
|
||||
<< "fail_on_expire timer ended in unknown error" << log::endl;
|
||||
terminate(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_endpoint.elog().at(log::elevel::DEVEL)
|
||||
<< "fail_on_expire timer expired" << log::endl;
|
||||
terminate(true);
|
||||
}
|
||||
|
||||
boost::asio::streambuf& buffer() {
|
||||
return m_buf;
|
||||
}
|
||||
|
||||
protected:
|
||||
endpoint_type& m_endpoint;
|
||||
char m_data[512]; // temporary
|
||||
endpoint_type& m_endpoint;
|
||||
|
||||
// Network resources
|
||||
boost::asio::streambuf m_buf;
|
||||
boost::asio::deadline_timer m_timer;
|
||||
|
||||
// WebSocket connection state
|
||||
session::state::value m_state;
|
||||
|
||||
// stuff that actually does the work
|
||||
processor::ptr m_processor;
|
||||
|
||||
|
||||
// Write queue
|
||||
std::queue<binary_string_ptr> m_write_queue;
|
||||
uint64_t m_write_buffer;
|
||||
write_state m_write_state;
|
||||
|
||||
// Close state
|
||||
close::status::value m_local_close_code;
|
||||
std::string m_local_close_reason;
|
||||
close::status::value m_remote_close_code;
|
||||
std::string m_remote_close_reason;
|
||||
bool m_closed_by_me;
|
||||
bool m_failed_by_me;
|
||||
bool m_dropped_by_me;
|
||||
};
|
||||
|
||||
// connection related types that it and its policy classes need.
|
||||
|
||||
@@ -92,6 +92,10 @@ public:
|
||||
void replace_header(const std::string &key,const std::string &val) {
|
||||
m_headers[key] = val;
|
||||
}
|
||||
|
||||
void remove_header(const std::string &key) {
|
||||
m_headers.erase(key);
|
||||
}
|
||||
protected:
|
||||
bool parse_headers(std::istream& s) {
|
||||
std::string header;
|
||||
@@ -229,6 +233,8 @@ public:
|
||||
raw << version() << " " << m_status_code << " " << m_status_msg << "\r\n";
|
||||
raw << raw_headers() << "\r\n";
|
||||
|
||||
raw << m_body;
|
||||
|
||||
return raw.str();
|
||||
}
|
||||
|
||||
@@ -244,6 +250,19 @@ public:
|
||||
m_status_msg = msg;
|
||||
}
|
||||
|
||||
void set_body(const std::string& value) {
|
||||
if (value.size() == 0) {
|
||||
remove_header("Content-Length");
|
||||
m_body = "";
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream foo;
|
||||
foo << value.size();
|
||||
replace_header("Content-Length", foo.str());
|
||||
m_body = value;
|
||||
}
|
||||
|
||||
status_code::value status_code() const {
|
||||
return m_status_code;
|
||||
}
|
||||
@@ -254,6 +273,7 @@ public:
|
||||
private:
|
||||
status_code::value m_status_code;
|
||||
std::string m_status_msg;
|
||||
std::string m_body;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
49
src/md5/md5.hpp
Normal file
49
src/md5/md5.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 WEBSOCKETPP_MD5_WRAPPER_HPP
|
||||
#define WEBSOCKETPP_MD5_WRAPPER_HPP
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
// could be compiled separately
|
||||
inline void md5_hash_string(const std::string& s);
|
||||
char digest[17];
|
||||
|
||||
md5_state_t state;
|
||||
|
||||
md5_init(&state);
|
||||
md5_append(&state, (const md5_byte_t *)s.c_str(), 16);
|
||||
md5_finish(&state, (md5_byte_t *)digest);
|
||||
|
||||
digest[16] = '\0';
|
||||
return std::string(digest);
|
||||
}
|
||||
|
||||
#endif // WEBSOCKETPP_MD5_WRAPPER_HPP
|
||||
@@ -26,10 +26,6 @@
|
||||
*/
|
||||
|
||||
#include "network_utilities.hpp"
|
||||
#include "websocket_constants.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include "md5/md5.h"
|
||||
|
||||
uint64_t htonll(uint64_t src) {
|
||||
static int typ = TYP_INIT;
|
||||
@@ -56,59 +52,6 @@ uint64_t ntohll(uint64_t src) {
|
||||
return htonll(src);
|
||||
}
|
||||
|
||||
std::string lookup_http_error_string(int code) {
|
||||
switch (code) {
|
||||
case 400:
|
||||
return "Bad Request";
|
||||
case 401:
|
||||
return "Unauthorized";
|
||||
case 403:
|
||||
return "Forbidden";
|
||||
case 404:
|
||||
return "Not Found";
|
||||
case 405:
|
||||
return "Method Not Allowed";
|
||||
case 406:
|
||||
return "Not Acceptable";
|
||||
case 407:
|
||||
return "Proxy Authentication Required";
|
||||
case 408:
|
||||
return "Request Timeout";
|
||||
case 409:
|
||||
return "Conflict";
|
||||
case 410:
|
||||
return "Gone";
|
||||
case 411:
|
||||
return "Length Required";
|
||||
case 412:
|
||||
return "Precondition Failed";
|
||||
case 413:
|
||||
return "Request Entity Too Large";
|
||||
case 414:
|
||||
return "Request-URI Too Long";
|
||||
case 415:
|
||||
return "Unsupported Media Type";
|
||||
case 416:
|
||||
return "Requested Range Not Satisfiable";
|
||||
case 417:
|
||||
return "Expectation Failed";
|
||||
case 500:
|
||||
return "Internal Server Error";
|
||||
case 501:
|
||||
return "Not Implimented";
|
||||
case 502:
|
||||
return "Bad Gateway";
|
||||
case 503:
|
||||
return "Service Unavailable";
|
||||
case 504:
|
||||
return "Gateway Timeout";
|
||||
case 505:
|
||||
return "HTTP Version Not Supported";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string lookup_ws_close_status_string(uint16_t code) {
|
||||
switch (code) {
|
||||
case 1000:
|
||||
@@ -136,103 +79,4 @@ std::string lookup_ws_close_status_string(uint16_t code) {
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool websocketpp::ws_uri::parse(const std::string& uri) {
|
||||
boost::cmatch what;
|
||||
static const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9:]+\\])(:\\d{1,5})?(/[^#]*)?");
|
||||
|
||||
// TODO: should this split resource into path/query?
|
||||
|
||||
if (boost::regex_match(uri.c_str(), what, expression)) {
|
||||
if (what[1] == "wss") {
|
||||
secure = true;
|
||||
} else {
|
||||
secure = false;
|
||||
}
|
||||
|
||||
host = what[2];
|
||||
|
||||
if (what[3] == "") {
|
||||
port = (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT);
|
||||
} else {
|
||||
unsigned int t_port = atoi(std::string(what[3]).substr(1).c_str());
|
||||
|
||||
if (t_port > 65535) {
|
||||
return false;
|
||||
}
|
||||
|
||||
port = atoi(std::string(what[3]).substr(1).c_str());
|
||||
}
|
||||
|
||||
if (what[4] == "") {
|
||||
resource = "/";
|
||||
} else {
|
||||
resource = what[4];
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string websocketpp::ws_uri::base() {
|
||||
std::stringstream s;
|
||||
|
||||
s << "ws" << (secure ? "s" : "") << "://" << host;
|
||||
|
||||
if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) {
|
||||
s << ":" << port;
|
||||
}
|
||||
|
||||
s << "/";
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::string websocketpp::ws_uri::str() {
|
||||
std::stringstream s;
|
||||
|
||||
s << "ws" << (secure ? "s" : "") << "://" << host;
|
||||
|
||||
if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) {
|
||||
s << ":" << port;
|
||||
}
|
||||
|
||||
s << resource;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
void md5_hash_string(char *string,char *hash) {
|
||||
md5_state_t state;
|
||||
|
||||
md5_init(&state);
|
||||
md5_append(&state, (const md5_byte_t *)string, 16);
|
||||
md5_finish(&state, (md5_byte_t *)hash);
|
||||
}
|
||||
|
||||
// Given a hybi 00 websocket key returns the 32 bit decoded value or 0 on error.
|
||||
uint32_t decode_hybi_00_client_key(const std::string& key) {
|
||||
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) {
|
||||
return htonl(num/spaces);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -30,9 +30,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
|
||||
|
||||
// http://www.viva64.com/en/k/0018/
|
||||
// TODO: impliment stuff from here:
|
||||
@@ -45,27 +42,6 @@
|
||||
uint64_t htonll(uint64_t src);
|
||||
uint64_t ntohll(uint64_t src);
|
||||
|
||||
std::string lookup_http_error_string(int code);
|
||||
std::string lookup_ws_close_status_string(uint16_t code);
|
||||
|
||||
namespace websocketpp {
|
||||
struct ws_uri {
|
||||
bool parse(const std::string& uri);
|
||||
std::string base();
|
||||
std::string str();
|
||||
|
||||
bool secure;
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
std::string resource;
|
||||
};
|
||||
}
|
||||
|
||||
// calculate the md5 hash of string and store it in the 16 byte hash buffer
|
||||
void md5_hash_string(char *string,char *hash);
|
||||
|
||||
|
||||
|
||||
uint32_t decode_hybi_00_client_key(const std::string& key);
|
||||
|
||||
#endif // NETWORK_UTILITIES_HPP
|
||||
|
||||
@@ -15,15 +15,41 @@ sockets/ssl.hpp
|
||||
#include "roles/server.hpp"
|
||||
#include "sockets/ssl.hpp"
|
||||
|
||||
typedef websocketpp::endpoint<websocketpp::role::server,websocketpp::socket::ssl> endpoint_type;
|
||||
typedef websocketpp::endpoint<websocketpp::role::server,websocketpp::socket::plain> endpoint_type;
|
||||
//typedef websocketpp::endpoint<websocketpp::role::server> endpoint_type;
|
||||
typedef websocketpp::role::server<endpoint_type>::handler handler_type;
|
||||
typedef websocketpp::role::server<endpoint_type>::handler_ptr handler_ptr;
|
||||
|
||||
// application headers
|
||||
class application_server_handler : public handler_type {
|
||||
void on_action() {
|
||||
std::cout << "application_server_handler::on_action()" << std::endl;
|
||||
public:
|
||||
void validate(handler_type::connection_ptr connection) {
|
||||
//std::cout << "state: " << connection->get_state() << std::endl;
|
||||
}
|
||||
|
||||
void on_open(handler_type::connection_ptr connection) {
|
||||
//std::cout << "connection opened" << std::endl;
|
||||
}
|
||||
|
||||
void on_close(handler_type::connection_ptr connection) {
|
||||
//std::cout << "connection closed" << std::endl;
|
||||
}
|
||||
|
||||
void on_message(connection_ptr connection,websocketpp::utf8_string_ptr msg) {
|
||||
//std::cout << "got message: " << *msg << std::endl;
|
||||
connection->send(*msg);
|
||||
}
|
||||
void on_message(connection_ptr connection,websocketpp::binary_string_ptr data) {
|
||||
//std::cout << "got binary message of length: " << data->size() << std::endl;
|
||||
connection->send(*data);
|
||||
}
|
||||
|
||||
void http(handler_type::connection_ptr connection) {
|
||||
connection->set_body("HTTP Response!!");
|
||||
}
|
||||
|
||||
void on_fail(handler_type::connection_ptr connection) {
|
||||
std::cout << "connection failed" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +69,7 @@ int main () {
|
||||
e.elog().set_level(websocketpp::log::elevel::ALL);
|
||||
|
||||
|
||||
e.listen(9000);
|
||||
e.listen(9002);
|
||||
//e.connect();
|
||||
//e.public_api();
|
||||
std::cout << std::endl;
|
||||
|
||||
@@ -25,21 +25,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKET_HYBI_PROCESSOR_HPP
|
||||
#define WEBSOCKET_HYBI_PROCESSOR_HPP
|
||||
#ifndef WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
|
||||
#include "interfaces/protocol.hpp"
|
||||
#include "processor.hpp"
|
||||
|
||||
#include "websocket_frame.hpp"
|
||||
#include "../base64/base64.h"
|
||||
#include "../sha1/sha1.h"
|
||||
|
||||
#include "network_utilities.hpp"
|
||||
#include "http/parser.hpp"
|
||||
|
||||
#include "base64/base64.h"
|
||||
#include "sha1/sha1.h"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace protocol {
|
||||
namespace processor {
|
||||
|
||||
namespace hybi_state {
|
||||
enum value {
|
||||
@@ -50,9 +47,9 @@ namespace hybi_state {
|
||||
}
|
||||
|
||||
template <class rng_policy>
|
||||
class hybi_processor : public processor {
|
||||
class hybi : public processor_base {
|
||||
public:
|
||||
hybi_processor(bool secure,rng_policy &rng) : m_secure(secure),m_fragmented_opcode(frame::opcode::CONTINUATION),m_utf8_payload(new utf8_string()),m_binary_payload(new binary_string()),m_read_frame(rng),m_write_frame(rng) {
|
||||
hybi(bool secure,rng_policy &rng) : m_secure(secure),m_fragmented_opcode(frame::opcode::CONTINUATION),m_utf8_payload(new utf8_string()),m_binary_payload(new binary_string()),m_read_frame(rng),m_write_frame(rng) {
|
||||
reset();
|
||||
}
|
||||
|
||||
@@ -128,35 +125,34 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
ws_uri get_uri(const http::parser::request& request) const {
|
||||
ws_uri uri;
|
||||
|
||||
uri.secure = m_secure;
|
||||
uri get_uri(const http::parser::request& request) const {
|
||||
uri connection_uri;
|
||||
|
||||
connection_uri.secure = m_secure;
|
||||
|
||||
std::string h = request.header("Host");
|
||||
|
||||
size_t found = h.find(":");
|
||||
if (found == std::string::npos) {
|
||||
uri.host = h;
|
||||
uri.port = (m_secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT);
|
||||
connection_uri.host = h;
|
||||
connection_uri.port = (m_secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT);
|
||||
} else {
|
||||
uint16_t p = atoi(h.substr(found+1).c_str());
|
||||
|
||||
if (p == 0) {
|
||||
throw(http::exception("Could not determine request uri. Check host header.",http::status_code::BAD_REQUEST));
|
||||
} else {
|
||||
uri.host = h.substr(0,found);
|
||||
uri.port = p;
|
||||
connection_uri.host = h.substr(0,found);
|
||||
connection_uri.port = p;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check if get_uri is a full uri
|
||||
uri.resource = request.uri();
|
||||
connection_uri.resource = request.uri();
|
||||
|
||||
std::cout << "parsed uri: " << uri.str() << std::endl;
|
||||
std::cout << "parsed uri: " << connection_uri.str() << std::endl;
|
||||
|
||||
return uri;
|
||||
return connection_uri;
|
||||
}
|
||||
|
||||
void handshake_response(const http::parser::request& request,http::parser::response& response) {
|
||||
@@ -201,57 +197,61 @@ public:
|
||||
m_read_frame.consume(s);
|
||||
|
||||
if (m_read_frame.ready()) {
|
||||
switch (m_read_frame.get_opcode()) {
|
||||
case frame::opcode::CONTINUATION:
|
||||
process_continuation();
|
||||
break;
|
||||
case frame::opcode::TEXT:
|
||||
process_text();
|
||||
break;
|
||||
case frame::opcode::BINARY:
|
||||
process_binary();
|
||||
break;
|
||||
case frame::opcode::CLOSE:
|
||||
if (!utf8_validator::validate(m_read_frame.get_close_msg())) {
|
||||
throw session::exception("Invalid UTF8",session::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
|
||||
m_opcode = frame::opcode::CLOSE;
|
||||
m_close_code = m_read_frame.get_close_status();
|
||||
m_close_reason = m_read_frame.get_close_msg();
|
||||
|
||||
break;
|
||||
case frame::opcode::PING:
|
||||
case frame::opcode::PONG:
|
||||
m_opcode = m_read_frame.get_opcode();
|
||||
extract_binary(m_control_payload);
|
||||
break;
|
||||
default:
|
||||
throw session::exception("Invalid Opcode",session::error::PROTOCOL_VIOLATION);
|
||||
break;
|
||||
}
|
||||
if (m_read_frame.get_fin()) {
|
||||
m_state = hybi_state::DONE;
|
||||
if (m_opcode == frame::opcode::TEXT) {
|
||||
if (!m_validator.complete()) {
|
||||
m_validator.reset();
|
||||
throw session::exception("Invalid UTF8",session::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
m_validator.reset();
|
||||
}
|
||||
}
|
||||
m_read_frame.reset();
|
||||
process_frame();
|
||||
}
|
||||
} catch (const frame::exception& e) {
|
||||
} catch (const processor::exception& e) {
|
||||
if (m_read_frame.ready()) {
|
||||
m_read_frame.reset();
|
||||
}
|
||||
|
||||
throw session::exception("Frame Error",session::error::PROTOCOL_VIOLATION);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process_frame() {
|
||||
switch (m_read_frame.get_opcode()) {
|
||||
case frame::opcode::CONTINUATION:
|
||||
process_continuation();
|
||||
break;
|
||||
case frame::opcode::TEXT:
|
||||
process_text();
|
||||
break;
|
||||
case frame::opcode::BINARY:
|
||||
process_binary();
|
||||
break;
|
||||
case frame::opcode::CLOSE:
|
||||
if (!utf8_validator::validate(m_read_frame.get_close_msg())) {
|
||||
throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
|
||||
m_opcode = frame::opcode::CLOSE;
|
||||
m_close_code = m_read_frame.get_close_status();
|
||||
m_close_reason = m_read_frame.get_close_msg();
|
||||
|
||||
break;
|
||||
case frame::opcode::PING:
|
||||
case frame::opcode::PONG:
|
||||
m_opcode = m_read_frame.get_opcode();
|
||||
extract_binary(m_control_payload);
|
||||
break;
|
||||
default:
|
||||
throw processor::exception("Invalid Opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
break;
|
||||
}
|
||||
if (m_read_frame.get_fin()) {
|
||||
m_state = hybi_state::DONE;
|
||||
if (m_opcode == frame::opcode::TEXT) {
|
||||
if (!m_validator.complete()) {
|
||||
m_validator.reset();
|
||||
throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
m_validator.reset();
|
||||
}
|
||||
}
|
||||
m_read_frame.reset();
|
||||
}
|
||||
|
||||
// frame type handlers:
|
||||
void process_continuation() {
|
||||
if (m_fragmented_opcode == frame::opcode::BINARY) {
|
||||
@@ -260,7 +260,7 @@ public:
|
||||
extract_utf8(m_utf8_payload);
|
||||
} else if (m_fragmented_opcode == frame::opcode::CONTINUATION) {
|
||||
// got continuation frame without a message to continue.
|
||||
throw session::exception("No message to continue.",session::error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("No message to continue.",processor::error::PROTOCOL_VIOLATION);
|
||||
} else {
|
||||
|
||||
// can't be here
|
||||
@@ -272,7 +272,7 @@ public:
|
||||
|
||||
void process_text() {
|
||||
if (m_fragmented_opcode != frame::opcode::CONTINUATION) {
|
||||
throw session::exception("New message started without closing previous.",session::error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("New message started without closing previous.",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
extract_utf8(m_utf8_payload);
|
||||
m_opcode = frame::opcode::TEXT;
|
||||
@@ -281,7 +281,7 @@ public:
|
||||
|
||||
void process_binary() {
|
||||
if (m_fragmented_opcode != frame::opcode::CONTINUATION) {
|
||||
throw session::exception("New message started without closing previous.",session::error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("New message started without closing previous.",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
m_opcode = frame::opcode::BINARY;
|
||||
m_fragmented_opcode = frame::opcode::BINARY;
|
||||
@@ -298,7 +298,7 @@ public:
|
||||
binary_string &msg = m_read_frame.get_payload();
|
||||
|
||||
if (!m_validator.decode(msg.begin(),msg.end())) {
|
||||
throw session::exception("Invalid UTF8",session::error::PAYLOAD_VIOLATION);
|
||||
throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
|
||||
dest->reserve(dest->size() + msg.size());
|
||||
@@ -477,9 +477,8 @@ private:
|
||||
frame::parser<rng_policy> m_read_frame;
|
||||
frame::parser<rng_policy> m_write_frame;
|
||||
};
|
||||
|
||||
//typedef boost::shared_ptr<hybi_processor> hybi_processor_ptr;
|
||||
|
||||
}
|
||||
}
|
||||
#endif // WEBSOCKET_HYBI_PROCESSOR_HPP
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
@@ -25,19 +25,60 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKET_INTERFACE_FRAME_PARSER_HPP
|
||||
#define WEBSOCKET_INTERFACE_FRAME_PARSER_HPP
|
||||
#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_SERVER_ERROR = 4, // cleanly end session
|
||||
MESSAGE_TOO_BIG = 5 // ???
|
||||
};
|
||||
}
|
||||
|
||||
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 <boost/shared_ptr.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace protocol {
|
||||
|
||||
class processor {
|
||||
namespace processor {
|
||||
|
||||
class processor_base {
|
||||
public:
|
||||
// validate client handshake
|
||||
// validate server handshake
|
||||
@@ -53,7 +94,7 @@ public:
|
||||
// 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 ws_uri get_uri(const http::parser::request& request) const = 0;
|
||||
virtual uri get_uri(const http::parser::request& request) const = 0;
|
||||
|
||||
// consume bytes, throw on exception
|
||||
virtual void consume(std::istream& s) = 0;
|
||||
@@ -88,8 +129,9 @@ public:
|
||||
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<processor> processor_ptr;
|
||||
typedef boost::shared_ptr<processor_base> ptr;
|
||||
|
||||
}
|
||||
}
|
||||
#endif // WEBSOCKET_INTERFACE_FRAME_PARSER_HPP
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HPP
|
||||
@@ -29,12 +29,16 @@
|
||||
#define WEBSOCKETPP_ROLE_SERVER_HPP
|
||||
|
||||
#include "../endpoint.hpp"
|
||||
#include "../processors/hybi.hpp"
|
||||
#include "../rng/blank_rng.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace role {
|
||||
@@ -46,49 +50,281 @@ public:
|
||||
template <typename connection_type>
|
||||
class connection {
|
||||
public:
|
||||
typedef connection<connection_type> type;
|
||||
typedef endpoint endpoint_type;
|
||||
|
||||
// Valid always
|
||||
std::string get_request_header(const std::string& key) const {}
|
||||
std::string get_origin() const {}
|
||||
unsigned int get_version() const {
|
||||
return m_version;
|
||||
}
|
||||
std::string get_request_header(const std::string& key) const {
|
||||
return m_request.header(key);
|
||||
}
|
||||
std::string get_origin() const {
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
// Information about the requested URI
|
||||
bool get_secure() const {
|
||||
return m_uri.secure;
|
||||
}
|
||||
std::string get_host() const {
|
||||
return m_uri.host;
|
||||
}
|
||||
std::string get_resource() const {
|
||||
return m_uri.resource;
|
||||
}
|
||||
unsigned short get_port() const {
|
||||
return m_uri.port;
|
||||
}
|
||||
|
||||
// Valid for CONNECTING state
|
||||
void add_response_header(const std::string& key, const std::string& value) {};
|
||||
void replace_response_header(const std::string& key, const std::string& e) {};
|
||||
const std::vector<std::string>& get_subprotocols() const {};
|
||||
const std::vector<std::string>& get_extensions() const {};
|
||||
void select_subprotocol(const std::string& value) {};
|
||||
void select_extension(const std::string& value) {};
|
||||
void add_response_header(const std::string& key, const std::string& value) {
|
||||
m_response.add_header(key,value);
|
||||
}
|
||||
void replace_response_header(const std::string& key, const std::string& value) {
|
||||
m_response.replace_header(key,value);
|
||||
}
|
||||
void remove_response_header(const std::string& key) {
|
||||
m_response.remove_header(key);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& get_subprotocols() const {
|
||||
return m_requested_subprotocols;
|
||||
}
|
||||
const std::vector<std::string>& get_extensions() const {
|
||||
return m_requested_extensions;
|
||||
}
|
||||
void select_subprotocol(const std::string& value) {
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_requested_subprotocols.begin(),
|
||||
m_requested_subprotocols.end(),
|
||||
value);
|
||||
|
||||
if (value != "" && it == m_requested_subprotocols.end()) {
|
||||
throw std::invalid_argument("Attempted to choose a subprotocol not proposed by the client");
|
||||
}
|
||||
|
||||
m_subprotocol = value;
|
||||
}
|
||||
void select_extension(const std::string& value) {
|
||||
if (value == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_requested_extensions.begin(),
|
||||
m_requested_extensions.end(),
|
||||
value);
|
||||
|
||||
if (it == m_requested_extensions.end()) {
|
||||
throw std::invalid_argument("Attempted to choose an extension not proposed by the client");
|
||||
}
|
||||
|
||||
m_extensions.push_back(value);
|
||||
}
|
||||
|
||||
// Valid if get_version() returns -1 (ie this is an http connection)
|
||||
void set_body(const std::string& value) {
|
||||
if (m_connection.m_version != -1) {
|
||||
// TODO: throw exception
|
||||
throw std::invalid_argument("set_body called from invalid state");
|
||||
}
|
||||
|
||||
m_response.set_body(value);
|
||||
}
|
||||
protected:
|
||||
//connection(server<endpoint>& e) : m_endpoint(e) {}
|
||||
connection(endpoint& e) : m_endpoint(e) {}
|
||||
connection(endpoint& e)
|
||||
: m_endpoint(e),
|
||||
m_connection(static_cast< connection_type& >(*this)),
|
||||
m_version(-1) {}
|
||||
|
||||
// initializes the websocket connection
|
||||
void async_init() {
|
||||
/*boost::asio::async_read_until(
|
||||
m_endpoint.socket(),
|
||||
m_buf,
|
||||
boost::asio::async_read_until(
|
||||
m_connection.get_socket(),
|
||||
m_connection.buffer(),
|
||||
"\r\n\r\n",
|
||||
boost::bind(
|
||||
&connection_type::handle_read_request,
|
||||
connection_type::shared_from_this(),
|
||||
&type::handle_read_request,
|
||||
m_connection.shared_from_this(), // shared from this?
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);*/
|
||||
);
|
||||
}
|
||||
|
||||
void handle_read_request(const boost::system::error_code& error,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if (error) {
|
||||
// log error
|
||||
m_endpoint.elog().at(log::elevel::ERROR) << "Error reading HTTP request. code: " << error << log::endl;
|
||||
m_connection.terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::istream request(&m_connection.buffer());
|
||||
|
||||
if (!m_request.parse_complete(request)) {
|
||||
// not a valid HTTP request/response
|
||||
throw http::exception("Recieved invalid HTTP Request",http::status_code::BAD_REQUEST);
|
||||
}
|
||||
|
||||
m_endpoint.alog().at(log::alevel::DEBUG_HANDSHAKE) << m_request.raw() << log::endl;
|
||||
|
||||
std::string h = m_request.header("Upgrade");
|
||||
if (boost::ifind_first(h,"websocket")) {
|
||||
h = m_request.header("Sec-WebSocket-Version");
|
||||
if (h == "") {
|
||||
// websocket upgrade is present but version is not.
|
||||
// assume hybi00
|
||||
m_version = 0;
|
||||
} else {
|
||||
m_version = atoi(h.c_str());
|
||||
if (m_version == 0) {
|
||||
throw(http::exception("Unable to determine connection version",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
|
||||
// create a websocket processor
|
||||
if (m_version == 0) {
|
||||
m_response.add_header("Sec-WebSocket-Version","13, 8, 7");
|
||||
|
||||
throw(http::exception("Unsupported WebSocket version",http::status_code::BAD_REQUEST));
|
||||
} else if (m_version == 7 ||
|
||||
m_version == 8 ||
|
||||
m_version == 13) {
|
||||
m_connection.m_processor = processor::ptr(new processor::hybi<blank_rng>(false,m_rng));
|
||||
} else {
|
||||
m_response.add_header("Sec-WebSocket-Version","13, 8, 7");
|
||||
|
||||
throw(http::exception("Unsupported WebSocket version",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
m_connection.m_processor->validate_handshake(m_request);
|
||||
m_origin = m_connection.m_processor->get_origin(m_request);
|
||||
m_uri = m_connection.m_processor->get_uri(m_request);
|
||||
|
||||
m_endpoint.get_handler()->validate(m_connection.shared_from_this());
|
||||
|
||||
m_response.set_status(http::status_code::SWITCHING_PROTOCOLS);
|
||||
} else {
|
||||
// continue as HTTP?
|
||||
m_endpoint.get_handler()->http(m_connection.shared_from_this());
|
||||
|
||||
m_response.set_status(http::status_code::OK);
|
||||
}
|
||||
} catch (const http::exception& e) {
|
||||
m_endpoint.elog().at(log::elevel::ERROR) << e.what() << log::endl;
|
||||
m_response.set_status(e.m_error_code,e.m_error_msg);
|
||||
m_response.set_body(e.m_body);
|
||||
}
|
||||
|
||||
write_response();
|
||||
}
|
||||
|
||||
void write_response() {
|
||||
m_response.set_version("HTTP/1.1");
|
||||
|
||||
if (m_response.status_code() == http::status_code::SWITCHING_PROTOCOLS) {
|
||||
// websocket response
|
||||
m_connection.m_processor->handshake_response(m_request,m_response);
|
||||
|
||||
if (m_subprotocol != "") {
|
||||
m_response.replace_header("Sec-WebSocket-Protocol",m_subprotocol);
|
||||
}
|
||||
|
||||
// TODO: return negotiated extensions
|
||||
} else {
|
||||
// HTTP response
|
||||
}
|
||||
|
||||
m_response.replace_header("Server","WebSocket++/2011-11-18");
|
||||
|
||||
std::string raw = m_response.raw();
|
||||
|
||||
m_endpoint.alog().at(log::alevel::DEBUG_HANDSHAKE) << raw << log::endl;
|
||||
|
||||
boost::asio::async_write(
|
||||
m_connection.get_socket(),
|
||||
boost::asio::buffer(raw),
|
||||
boost::bind(
|
||||
&type::handle_write_response,
|
||||
m_connection.shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_write_response(const boost::system::error_code& error) {
|
||||
// TODO: handshake timer
|
||||
|
||||
if (error) {
|
||||
m_endpoint.elog().at(log::elevel::ERROR) << "Network error writing handshake respons. code: " << error << log::endl;
|
||||
|
||||
m_connection.terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
log_open_result();
|
||||
|
||||
if (m_response.status_code() != http::status_code::SWITCHING_PROTOCOLS) {
|
||||
if (m_version == -1) {
|
||||
// if this was not a websocket connection, we have written
|
||||
// the expected response and the connection can be closed.
|
||||
} else {
|
||||
// this was a websocket connection that ended in an error
|
||||
m_endpoint.elog().at(log::elevel::ERROR)
|
||||
<< "Handshake ended with HTTP error: "
|
||||
<< m_response.status_code() << " "
|
||||
<< m_response.status_msg() << log::endl;
|
||||
}
|
||||
m_connection.terminate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection.m_state = session::state::OPEN;
|
||||
|
||||
m_endpoint.get_handler()->on_open(m_connection.shared_from_this());
|
||||
|
||||
m_connection.handle_read_frame(boost::system::error_code());
|
||||
}
|
||||
|
||||
void log_open_result() {
|
||||
std::stringstream version;
|
||||
version << "v" << m_version << " ";
|
||||
|
||||
m_endpoint.alog().at(log::alevel::CONNECT) << (m_version == -1 ? "HTTP" : "WebSocket") << " Connection "
|
||||
<< m_connection.get_raw_socket().remote_endpoint() << " "
|
||||
<< (m_version == -1 ? "" : version.str())
|
||||
<< (get_request_header("User-Agent") == "" ? "NULL" : get_request_header("User-Agent"))
|
||||
<< " " << m_uri.resource << " " << m_response.status_code()
|
||||
<< log::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
endpoint& m_endpoint;
|
||||
connection_type& m_connection;
|
||||
|
||||
int m_version;
|
||||
uri m_uri;
|
||||
std::string m_origin;
|
||||
std::vector<std::string> m_requested_subprotocols;
|
||||
std::vector<std::string> m_requested_extensions;
|
||||
std::string m_subprotocol;
|
||||
std::vector<std::string> m_extensions;
|
||||
|
||||
http::parser::request m_request;
|
||||
http::parser::response m_response;
|
||||
blank_rng m_rng;
|
||||
};
|
||||
|
||||
// handler interface callback class
|
||||
class handler {
|
||||
virtual void on_action() = 0;
|
||||
};
|
||||
class handler;
|
||||
|
||||
typedef boost::shared_ptr<handler> handler_ptr;
|
||||
|
||||
@@ -97,7 +333,27 @@ public:
|
||||
typedef endpoint endpoint_type;
|
||||
|
||||
typedef typename endpoint_traits<endpoint>::connection_ptr connection_ptr;
|
||||
|
||||
// handler interface callback class
|
||||
class handler {
|
||||
public:
|
||||
typedef connection_ptr connection_ptr;
|
||||
|
||||
// Required
|
||||
virtual void validate(connection_ptr connection) = 0;
|
||||
virtual void on_open(connection_ptr connection) = 0;
|
||||
virtual void on_close(connection_ptr connection) = 0;
|
||||
|
||||
virtual void on_message(connection_ptr connection,utf8_string_ptr) = 0;
|
||||
virtual void on_message(connection_ptr connection,binary_string_ptr) = 0;
|
||||
|
||||
// Optional
|
||||
virtual bool on_ping(connection_ptr connection,binary_string_ptr) {return true;}
|
||||
virtual void on_pong(connection_ptr connection,binary_string_ptr) {}
|
||||
virtual void http(connection_ptr connection) {}
|
||||
virtual void on_fail(connection_ptr connection) {}
|
||||
};
|
||||
|
||||
server(boost::asio::io_service& m,handler_ptr h)
|
||||
: m_ws_endpoint(static_cast< endpoint_type& >(*this)),
|
||||
m_handler(h),
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
using websocketpp::SHA1;
|
||||
|
||||
/*
|
||||
* SHA1
|
||||
*
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
#ifndef _SHA1_H_
|
||||
#define _SHA1_H_
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
class SHA1
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
SHA1();
|
||||
@@ -86,4 +87,6 @@ class SHA1
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // _SHA1_H_
|
||||
|
||||
@@ -49,6 +49,15 @@ public:
|
||||
template <typename connection_type>
|
||||
class connection {
|
||||
public:
|
||||
// should these two be public or protected. If protected, how?
|
||||
boost::asio::ip::tcp::socket& get_raw_socket() {
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket& get_socket() {
|
||||
return m_socket;
|
||||
}
|
||||
protected:
|
||||
connection(plain<endpoint_type>& e) : m_socket(e.get_io_service()) {}
|
||||
|
||||
void async_init(socket_init_callback callback) {
|
||||
@@ -56,12 +65,15 @@ public:
|
||||
callback(boost::system::error_code());
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket& get_raw_socket() {
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket& get_socket() {
|
||||
return m_socket;
|
||||
bool shutdown() {
|
||||
boost::system::error_code ignored_ec;
|
||||
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both,ignored_ec);
|
||||
|
||||
if (ignored_ec) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
boost::asio::ip::tcp::socket m_socket;
|
||||
|
||||
@@ -72,6 +72,15 @@ public:
|
||||
template <typename connection_type>
|
||||
class connection {
|
||||
public:
|
||||
// should these two be public or protected. If protected, how?
|
||||
ssl_socket::lowest_layer_type& get_raw_socket() {
|
||||
return m_socket.lowest_layer();
|
||||
}
|
||||
|
||||
ssl_socket& get_socket() {
|
||||
return m_socket;
|
||||
}
|
||||
protected:
|
||||
connection(ssl<endpoint_type>& e) : m_socket(e.get_io_service(),e.get_context()),m_endpoint(e) {}
|
||||
|
||||
void async_init(boost::function<void(const boost::system::error_code&)> callback) {
|
||||
@@ -98,12 +107,15 @@ public:
|
||||
callback(error);
|
||||
}
|
||||
|
||||
ssl_socket::lowest_layer_type& get_raw_socket() {
|
||||
return m_socket.lowest_layer();
|
||||
}
|
||||
|
||||
ssl_socket& get_socket() {
|
||||
return m_socket;
|
||||
bool shutdown() {
|
||||
boost::system::error_code ignored_ec;
|
||||
m_socket.shutdown(ignored_ec);
|
||||
|
||||
if (ignored_ec) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
ssl_socket m_socket;
|
||||
@@ -119,9 +131,9 @@ protected:
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
m_context.set_password_callback(boost::bind(&type::get_password, this));
|
||||
m_context.use_certificate_chain_file("/Users/zaphoyd/Documents/ZS/websocketpp/src/ssl/server.pem");
|
||||
m_context.use_private_key_file("/Users/zaphoyd/Documents/ZS/websocketpp/src/ssl/server.pem", boost::asio::ssl::context::pem);
|
||||
m_context.use_tmp_dh_file("/Users/zaphoyd/Documents/ZS/websocketpp/src/ssl/dh512.pem");
|
||||
m_context.use_certificate_chain_file("/Users/zaphoyd/Documents/websocketpp/src/ssl/server.pem");
|
||||
m_context.use_private_key_file("/Users/zaphoyd/Documents/websocketpp/src/ssl/server.pem", boost::asio::ssl::context::pem);
|
||||
m_context.use_tmp_dh_file("/Users/zaphoyd/Documents/websocketpp/src/ssl/dh512.pem");
|
||||
} catch (std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
112
src/uri.hpp
Normal file
112
src/uri.hpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 WEBSOCKETPP_URI_HPP
|
||||
#define WEBSOCKETPP_URI_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
struct uri {
|
||||
bool parse(const std::string& uri) {
|
||||
boost::cmatch what;
|
||||
static const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9:]+\\])(:\\d{1,5})?(/[^#]*)?");
|
||||
|
||||
// TODO: should this split resource into path/query?
|
||||
|
||||
if (boost::regex_match(uri.c_str(), what, expression)) {
|
||||
if (what[1] == "wss") {
|
||||
secure = true;
|
||||
} else {
|
||||
secure = false;
|
||||
}
|
||||
|
||||
host = what[2];
|
||||
|
||||
if (what[3] == "") {
|
||||
port = (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT);
|
||||
} else {
|
||||
unsigned int t_port = atoi(std::string(what[3]).substr(1).c_str());
|
||||
|
||||
if (t_port > 65535) {
|
||||
return false;
|
||||
}
|
||||
|
||||
port = atoi(std::string(what[3]).substr(1).c_str());
|
||||
}
|
||||
|
||||
if (what[4] == "") {
|
||||
resource = "/";
|
||||
} else {
|
||||
resource = what[4];
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
std::string base() {
|
||||
std::stringstream s;
|
||||
|
||||
s << "ws" << (secure ? "s" : "") << "://" << host;
|
||||
|
||||
if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) {
|
||||
s << ":" << port;
|
||||
}
|
||||
|
||||
s << "/";
|
||||
return s.str();
|
||||
|
||||
}
|
||||
std::string str() {
|
||||
std::stringstream s;
|
||||
|
||||
s << "ws" << (secure ? "s" : "") << "://" << host;
|
||||
|
||||
if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) {
|
||||
s << ":" << port;
|
||||
}
|
||||
|
||||
s << resource;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
bool secure;
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
std::string resource;
|
||||
};
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_URI_HPP
|
||||
@@ -28,10 +28,54 @@
|
||||
#ifndef WEBSOCKET_FRAME_HPP
|
||||
#define WEBSOCKET_FRAME_HPP
|
||||
|
||||
#include "network_utilities.hpp"
|
||||
#include "websocket_constants.hpp"
|
||||
namespace websocketpp {
|
||||
namespace frame {
|
||||
// Opcodes are 4 bits
|
||||
// See spec section 5.2
|
||||
namespace opcode {
|
||||
enum value {
|
||||
CONTINUATION = 0x0,
|
||||
TEXT = 0x1,
|
||||
BINARY = 0x2,
|
||||
RSV3 = 0x3,
|
||||
RSV4 = 0x4,
|
||||
RSV5 = 0x5,
|
||||
RSV6 = 0x6,
|
||||
RSV7 = 0x7,
|
||||
CLOSE = 0x8,
|
||||
PING = 0x9,
|
||||
PONG = 0xA,
|
||||
CONTROL_RSVB = 0xB,
|
||||
CONTROL_RSVC = 0xC,
|
||||
CONTROL_RSVD = 0xD,
|
||||
CONTROL_RSVE = 0xE,
|
||||
CONTROL_RSVF = 0xF,
|
||||
};
|
||||
|
||||
inline bool reserved(value v) {
|
||||
return (v >= RSV3 && v <= RSV7) ||
|
||||
(v >= CONTROL_RSVB && v <= CONTROL_RSVF);
|
||||
}
|
||||
|
||||
inline bool invalid(value v) {
|
||||
return (v > 0xF || v < 0);
|
||||
}
|
||||
|
||||
inline bool is_control(value v) {
|
||||
return v >= 0x8;
|
||||
}
|
||||
}
|
||||
|
||||
namespace limits {
|
||||
static const uint8_t PAYLOAD_SIZE_BASIC = 125;
|
||||
static const uint16_t PAYLOAD_SIZE_EXTENDED = 0xFFFF; // 2^16, 65535
|
||||
static const uint64_t PAYLOAD_SIZE_JUMBO = 0x7FFFFFFFFFFFFFFF;//2^63
|
||||
}
|
||||
} // namespace frame
|
||||
} // namespace websocketpp
|
||||
|
||||
#include "websocket_server.hpp" // for server error. this should be gotten rid of
|
||||
#include "network_utilities.hpp"
|
||||
#include "processor.hpp"
|
||||
#include "utf8_validator/utf8_validator.hpp"
|
||||
|
||||
#if defined(WIN32)
|
||||
@@ -67,27 +111,7 @@ private:
|
||||
boost::random::variate_generator<boost::random::random_device&,boost::random::uniform_int_distribution<> > m_gen;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// Exception classes
|
||||
class exception : public std::exception {
|
||||
public:
|
||||
exception(const std::string& msg,
|
||||
frame::error::value code = frame::error::FATAL_SESSION_ERROR)
|
||||
: m_msg(msg),m_code(code) {}
|
||||
~exception() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
frame::error::value code() const throw() {
|
||||
return m_code;
|
||||
}
|
||||
|
||||
std::string m_msg;
|
||||
frame::error::value m_code;
|
||||
};
|
||||
*/
|
||||
|
||||
template <class rng_policy>
|
||||
class parser {
|
||||
@@ -212,12 +236,12 @@ public:
|
||||
/*if (s.gcount() == 0) {
|
||||
throw frame_error("consume read zero bytes",FERR_FATAL_SESSION_ERROR);
|
||||
}*/
|
||||
} catch (const exception& e) {
|
||||
} catch (const processor::exception& e) {
|
||||
// After this point all non-close frames must be considered garbage,
|
||||
// including the current one. Reset it and put the reading frame into
|
||||
// a recovery state.
|
||||
if (m_degraded == true) {
|
||||
throw exception("An error occurred while trying to gracefully recover from a less serious frame error.",error::FATAL_SESSION_ERROR);
|
||||
throw processor::exception("An error occurred while trying to gracefully recover from a less serious frame error.",processor::error::FATAL_ERROR);
|
||||
} else {
|
||||
reset();
|
||||
m_state = STATE_RECOVERY;
|
||||
@@ -253,7 +277,7 @@ public:
|
||||
|
||||
char* get_masking_key() {
|
||||
if (m_state != STATE_READY) {
|
||||
throw exception("attempted to get masking_key before reading full header");
|
||||
throw processor::exception("attempted to get masking_key before reading full header");
|
||||
}
|
||||
return m_header[get_header_len()-4];
|
||||
}
|
||||
@@ -307,16 +331,16 @@ public:
|
||||
return frame::opcode::value(m_header[0] & BPB0_OPCODE);
|
||||
}
|
||||
void set_opcode(opcode::value op) {
|
||||
if (frame::opcode::reserved(op)) {
|
||||
throw exception("reserved opcode",error::PROTOCOL_VIOLATION);
|
||||
if (opcode::reserved(op)) {
|
||||
throw processor::exception("reserved opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (frame::opcode::invalid(op)) {
|
||||
throw exception("invalid opcode",error::PROTOCOL_VIOLATION);
|
||||
if (opcode::invalid(op)) {
|
||||
throw processor::exception("invalid opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw exception("control frames can't have large payloads",error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("control frames can't have large payloads",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits
|
||||
@@ -370,7 +394,7 @@ public:
|
||||
uint32_t codep = 0;
|
||||
validate_utf8(&state,&codep,2);
|
||||
if (state != utf8_validator::UTF8_ACCEPT) {
|
||||
throw exception("Invalid UTF-8 Data",error::PAYLOAD_VIOLATION);
|
||||
throw processor::exception("Invalid UTF-8 Data",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
return std::string(m_payload.begin()+2,m_payload.end());
|
||||
} else {
|
||||
@@ -394,12 +418,12 @@ public:
|
||||
}
|
||||
void set_payload_helper(size_t s) {
|
||||
if (s > max_payload_size) {
|
||||
throw exception("requested payload is over implimentation defined limit",error::MESSAGE_TOO_BIG);
|
||||
throw processor::exception("requested payload is over implimentation defined limit",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
|
||||
// limits imposed by the websocket spec
|
||||
if (is_control() && s > limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw exception("control frames can't have large payloads",error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("control frames can't have large payloads",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (s <= limits::PAYLOAD_SIZE_BASIC) {
|
||||
@@ -415,7 +439,7 @@ public:
|
||||
m_header[1] = BASIC_PAYLOAD_64BIT_CODE;
|
||||
*reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH]) = htonll(s);
|
||||
} else {
|
||||
throw exception("payload size limit is 63 bits",error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("payload size limit is 63 bits",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
m_payload.resize(s);
|
||||
@@ -426,13 +450,13 @@ public:
|
||||
if (close::status::invalid(status)) {
|
||||
std::stringstream err;
|
||||
err << "Status code " << status << " is invalid";
|
||||
throw exception(err.str());
|
||||
throw processor::exception(err.str());
|
||||
}
|
||||
|
||||
if (close::status::reserved(status)) {
|
||||
std::stringstream err;
|
||||
err << "Status code " << status << " is reserved";
|
||||
throw exception(err.str());
|
||||
throw processor::exception(err.str());
|
||||
}
|
||||
|
||||
m_payload.resize(2+message.size());
|
||||
@@ -450,7 +474,7 @@ public:
|
||||
}
|
||||
|
||||
bool is_control() const {
|
||||
return (frame::opcode::is_control(get_opcode()));
|
||||
return (opcode::is_control(get_opcode()));
|
||||
}
|
||||
|
||||
std::string print_frame() const {
|
||||
@@ -490,14 +514,14 @@ public:
|
||||
// reinterpret the second two bytes as a 16 bit integer in network
|
||||
// byte order. Convert to host byte order and store locally.
|
||||
payload_size = ntohs(*(
|
||||
reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (payload_size < s) {
|
||||
std::stringstream err;
|
||||
err << "payload length not minimally encoded. Using 16 bit form for payload size: " << payload_size;
|
||||
m_bytes_needed = payload_size;
|
||||
throw exception(err.str(),error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception(err.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
mask_index += 2;
|
||||
@@ -505,19 +529,19 @@ public:
|
||||
// reinterpret the second eight bytes as a 64 bit integer in
|
||||
// network byte order. Convert to host byte order and store.
|
||||
payload_size = ntohll(*(
|
||||
reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (payload_size <= limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
m_bytes_needed = payload_size;
|
||||
throw exception("payload length not minimally encoded",
|
||||
error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("payload length not minimally encoded",
|
||||
processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
mask_index += 8;
|
||||
} else {
|
||||
// TODO: shouldn't be here how to handle?
|
||||
throw exception("invalid get_basic_size in process_extended_header");
|
||||
throw processor::exception("invalid get_basic_size in process_extended_header");
|
||||
}
|
||||
|
||||
if (get_masked() == 0) {
|
||||
@@ -586,29 +610,29 @@ public:
|
||||
using utf8_validator::decode;
|
||||
|
||||
if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) {
|
||||
throw exception("Invalid UTF-8 Data",error::PAYLOAD_VIOLATION);
|
||||
throw processor::exception("Invalid UTF-8 Data",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
void validate_basic_header() const {
|
||||
// check for control frame size
|
||||
if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw exception("Control Frame is too large",error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("Control Frame is too large",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved bits
|
||||
if (get_rsv1() || get_rsv2() || get_rsv3()) {
|
||||
throw exception("Reserved bit used",error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("Reserved bit used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved opcodes
|
||||
if (opcode::reserved(get_opcode())) {
|
||||
throw exception("Reserved opcode used",error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("Reserved opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for fragmented control message
|
||||
if (is_control() && !get_fin()) {
|
||||
throw exception("Fragmented control message",error::PROTOCOL_VIOLATION);
|
||||
throw processor::exception("Fragmented control message",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,106 +662,6 @@ private:
|
||||
rng_policy& m_rng;
|
||||
};
|
||||
|
||||
class hybi_00_parser {
|
||||
public:
|
||||
// TODO: not hardcode this
|
||||
static const uint64_t max_payload_size = 100000000; // 100MB
|
||||
|
||||
hybi_00_parser() : m_state(STATE_READ_TYPE) {
|
||||
reset();
|
||||
}
|
||||
|
||||
bool ready() const {
|
||||
return m_state == STATE_READY;
|
||||
}
|
||||
uint64_t get_bytes_needed() const {
|
||||
return 1;
|
||||
}
|
||||
void reset() {
|
||||
m_state = STATE_READ_TYPE;
|
||||
}
|
||||
|
||||
void set_fin(bool fin) {}
|
||||
void set_opcode(opcode::value op) {
|
||||
if (op != frame::opcode::TEXT) {
|
||||
// TODO: what happens when you try to send non-text to a hybi_00 frame
|
||||
}
|
||||
}
|
||||
|
||||
// hybi_00 frames are UTF-8 text only.
|
||||
opcode::value get_opcode() const {
|
||||
return frame::opcode::TEXT;
|
||||
}
|
||||
|
||||
void set_payload(const std::string source) {
|
||||
if (source.size() > max_payload_size) {
|
||||
throw exception("requested payload is over implimentation defined limit",error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
|
||||
// TODO: utf8 validation?
|
||||
|
||||
m_payload.resize(source.size()+2);
|
||||
|
||||
m_payload[0] = 0x00;
|
||||
std::copy(source.begin(),source.end(),m_payload.begin()+1);
|
||||
m_payload[m_payload.size()-1] = 0xFF;
|
||||
}
|
||||
|
||||
void set_masked(bool masked) {}
|
||||
|
||||
void process_payload() {}
|
||||
|
||||
bool is_control() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> &get_payload() {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
char* get_header() {
|
||||
// TODO: this might be a problem
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int get_header_len() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void validate_utf8(uint32_t* state,uint32_t* codep,size_t offset = 0) const {
|
||||
for (size_t i = offset; i < m_payload.size(); i++) {
|
||||
using utf8_validator::decode;
|
||||
|
||||
if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) {
|
||||
throw exception("Invalid UTF-8 Data",error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method invariant: One of the following must always be true even in the case
|
||||
// of exceptions.
|
||||
// - m_bytes_needed > 0
|
||||
// - m-state = STATE_READY
|
||||
void consume(std::istream &s) {
|
||||
// read a byte. if it is 0x00 then read payload bytes until 0xFF.
|
||||
// otherwise it may be another type of frame. Test whether or not
|
||||
// hybi00 clients actually send them.
|
||||
|
||||
// should do streaming utf8 validation and throw an exception on error.
|
||||
}
|
||||
private:
|
||||
static const uint8_t STATE_READ_TYPE = 1;
|
||||
static const uint8_t STATE_READ_LENGTH = 2;
|
||||
static const uint8_t STATE_READ_PAYLOAD = 3;
|
||||
static const uint8_t STATE_READY = 4;
|
||||
|
||||
uint8_t m_state;
|
||||
std::vector<unsigned char> m_payload;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
B613878F145CA61300ED9B19 /* libboost_date_time.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CBE1434AF6A0029A1B1 /* libboost_date_time.dylib */; };
|
||||
B6138790145CA61C00ED9B19 /* libboost_program_options.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6FE8CEB145A0F1900B32547 /* libboost_program_options.dylib */; };
|
||||
B62A5A7214775ECF005F9EB0 /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; };
|
||||
B68288871437460E002BA48B /* chat_client_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6828875143745DA002BA48B /* chat_client_handler.cpp */; };
|
||||
B68288881437460E002BA48B /* chat_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6828877143745DA002BA48B /* chat_client.cpp */; };
|
||||
B682888914374617002BA48B /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; };
|
||||
B682888B14374623002BA48B /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B682888A14374623002BA48B /* libboost_system.dylib */; };
|
||||
B682888D1437464A002BA48B /* libboost_random.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B682888C1437464A002BA48B /* libboost_random.dylib */; };
|
||||
B682888F14374689002BA48B /* libboost_thread.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B682888E14374689002BA48B /* libboost_thread.dylib */; };
|
||||
B6BE76EA144EF53000716A77 /* websocket_endpoint.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6BE76E9144EF53000716A77 /* websocket_endpoint.hpp */; };
|
||||
B6BE76EB144EF53000716A77 /* websocket_endpoint.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6BE76E9144EF53000716A77 /* websocket_endpoint.hpp */; };
|
||||
@@ -34,41 +34,28 @@
|
||||
B6DF1C8D1434AC330029A1B1 /* sha1.h in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C891434AC330029A1B1 /* sha1.h */; };
|
||||
B6DF1C901434AC3E0029A1B1 /* utf8_validator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C8F1434AC3E0029A1B1 /* utf8_validator.hpp */; };
|
||||
B6DF1C911434AC3E0029A1B1 /* utf8_validator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C8F1434AC3E0029A1B1 /* utf8_validator.hpp */; };
|
||||
B6DF1CA01434AC470029A1B1 /* websocket_client_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C921434AC470029A1B1 /* websocket_client_session.cpp */; };
|
||||
B6DF1CA21434AC470029A1B1 /* websocket_client_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C931434AC470029A1B1 /* websocket_client_session.hpp */; };
|
||||
B6DF1CA31434AC470029A1B1 /* websocket_client_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C931434AC470029A1B1 /* websocket_client_session.hpp */; };
|
||||
B6DF1CA41434AC470029A1B1 /* websocket_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C941434AC470029A1B1 /* websocket_client.cpp */; };
|
||||
B6DF1CA61434AC470029A1B1 /* websocket_client.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C951434AC470029A1B1 /* websocket_client.hpp */; };
|
||||
B6DF1CA71434AC470029A1B1 /* websocket_client.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C951434AC470029A1B1 /* websocket_client.hpp */; };
|
||||
B6DF1CA81434AC470029A1B1 /* websocket_connection_handler.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C961434AC470029A1B1 /* websocket_connection_handler.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B6DF1CA91434AC470029A1B1 /* websocket_connection_handler.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C961434AC470029A1B1 /* websocket_connection_handler.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B6DF1CAA1434AC470029A1B1 /* websocket_frame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C971434AC470029A1B1 /* websocket_frame.cpp */; };
|
||||
B6DF1CAB1434AC470029A1B1 /* websocket_frame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C971434AC470029A1B1 /* websocket_frame.cpp */; };
|
||||
B6DF1CAC1434AC470029A1B1 /* websocket_frame.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C981434AC470029A1B1 /* websocket_frame.hpp */; };
|
||||
B6DF1CAD1434AC470029A1B1 /* websocket_frame.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C981434AC470029A1B1 /* websocket_frame.hpp */; };
|
||||
B6DF1CAE1434AC470029A1B1 /* websocket_server_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C991434AC470029A1B1 /* websocket_server_session.cpp */; };
|
||||
B6DF1CB01434AC470029A1B1 /* websocket_server_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9A1434AC470029A1B1 /* websocket_server_session.hpp */; };
|
||||
B6DF1CB11434AC470029A1B1 /* websocket_server_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9A1434AC470029A1B1 /* websocket_server_session.hpp */; };
|
||||
B6DF1CB21434AC470029A1B1 /* websocket_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9B1434AC470029A1B1 /* websocket_server.cpp */; };
|
||||
B6DF1CB31434AC470029A1B1 /* websocket_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9B1434AC470029A1B1 /* websocket_server.cpp */; };
|
||||
B6DF1CB41434AC470029A1B1 /* websocket_server.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9C1434AC470029A1B1 /* websocket_server.hpp */; };
|
||||
B6DF1CB51434AC470029A1B1 /* websocket_server.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9C1434AC470029A1B1 /* websocket_server.hpp */; };
|
||||
B6DF1CB61434AC470029A1B1 /* websocket_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9D1434AC470029A1B1 /* websocket_session.cpp */; };
|
||||
B6DF1CB71434AC470029A1B1 /* websocket_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9D1434AC470029A1B1 /* websocket_session.cpp */; };
|
||||
B6DF1CB81434AC470029A1B1 /* websocket_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9E1434AC470029A1B1 /* websocket_session.hpp */; };
|
||||
B6DF1CB91434AC470029A1B1 /* websocket_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9E1434AC470029A1B1 /* websocket_session.hpp */; };
|
||||
B6DF1CBA1434AC470029A1B1 /* websocketpp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9F1434AC470029A1B1 /* websocketpp.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B6DF1CBB1434AC470029A1B1 /* websocketpp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9F1434AC470029A1B1 /* websocketpp.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B6DF1CC11434AF6A0029A1B1 /* libboost_date_time.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CBE1434AF6A0029A1B1 /* libboost_date_time.dylib */; };
|
||||
B6DF1CC21434AF6A0029A1B1 /* libboost_regex.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CBF1434AF6A0029A1B1 /* libboost_regex.dylib */; };
|
||||
B6DF1CC31434AF6A0029A1B1 /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CC01434AF6A0029A1B1 /* libboost_system.dylib */; };
|
||||
B6DF1CC41434AF9E0029A1B1 /* network_utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C791434AB740029A1B1 /* network_utilities.cpp */; };
|
||||
B6DF1CDB1435EDCE0029A1B1 /* echo_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1CCA1435ED760029A1B1 /* echo_server.cpp */; };
|
||||
B6DF1CDC1435EDCE0029A1B1 /* echo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1CCB1435ED760029A1B1 /* echo.cpp */; };
|
||||
B6DF1CDE1435EDF00029A1B1 /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; };
|
||||
B6DF1CE21435F1860029A1B1 /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CE11435F1860029A1B1 /* libboost_system.dylib */; };
|
||||
B6DF1CE41435F8250029A1B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CE31435F8250029A1B1 /* Foundation.framework */; };
|
||||
B6FE8CEC145A0F1900B32547 /* libboost_program_options.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6FE8CEB145A0F1900B32547 /* libboost_program_options.dylib */; };
|
||||
B6FE8D06145AFF5F00B32547 /* websocket_constants.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6FE8D05145AFF5F00B32547 /* websocket_constants.hpp */; };
|
||||
B6FE8D07145AFF5F00B32547 /* websocket_constants.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6FE8D05145AFF5F00B32547 /* websocket_constants.hpp */; };
|
||||
B6FE8D181468707200B32547 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = B6FE8D1614686A8500B32547 /* md5.c */; };
|
||||
@@ -168,6 +155,12 @@
|
||||
B62A5A581473EBF1005F9EB0 /* plain.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = plain.hpp; path = src/sockets/plain.hpp; sourceTree = "<group>"; };
|
||||
B62A5A591473EBF1005F9EB0 /* ssl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ssl.hpp; path = src/sockets/ssl.hpp; sourceTree = "<group>"; };
|
||||
B62A5A6114755443005F9EB0 /* socket_base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = socket_base.hpp; path = src/sockets/socket_base.hpp; sourceTree = "<group>"; };
|
||||
B62A5A6B147748B2005F9EB0 /* hybi.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = hybi.hpp; sourceTree = "<group>"; };
|
||||
B62A5A6C147748B2005F9EB0 /* hybi_legacy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = hybi_legacy.hpp; sourceTree = "<group>"; };
|
||||
B62A5A6D147748B2005F9EB0 /* processor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = processor.hpp; sourceTree = "<group>"; };
|
||||
B62A5A6F14774CD5005F9EB0 /* uri.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = uri.hpp; path = src/uri.hpp; sourceTree = "<group>"; };
|
||||
B62A5A7014774F08005F9EB0 /* md5.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = md5.hpp; path = src/md5/md5.hpp; sourceTree = "<group>"; };
|
||||
B62A5A71147759EA005F9EB0 /* common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = common.hpp; path = src/common.hpp; sourceTree = "<group>"; };
|
||||
B6828875143745DA002BA48B /* chat_client_handler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client_handler.cpp; path = examples/chat_client/chat_client_handler.cpp; sourceTree = "<group>"; };
|
||||
B6828876143745DA002BA48B /* chat_client_handler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = chat_client_handler.hpp; path = examples/chat_client/chat_client_handler.hpp; sourceTree = "<group>"; };
|
||||
B6828877143745DA002BA48B /* chat_client.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client.cpp; path = examples/chat_client/chat_client.cpp; sourceTree = "<group>"; };
|
||||
@@ -261,11 +254,6 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B6FE8CEC145A0F1900B32547 /* libboost_program_options.dylib in Frameworks */,
|
||||
B682888D1437464A002BA48B /* libboost_random.dylib in Frameworks */,
|
||||
B6DF1CC11434AF6A0029A1B1 /* libboost_date_time.dylib in Frameworks */,
|
||||
B6DF1CC21434AF6A0029A1B1 /* libboost_regex.dylib in Frameworks */,
|
||||
B6DF1CC31434AF6A0029A1B1 /* libboost_system.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -285,6 +273,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B62A5A7214775ECF005F9EB0 /* libwebsocketpp.dylib in Frameworks */,
|
||||
B6FE8D6614757D6400B32547 /* libboost_date_time.dylib in Frameworks */,
|
||||
B6FE8D5E14730B2200B32547 /* libssl.dylib in Frameworks */,
|
||||
B6FE8D5C14730B1A00B32547 /* libcrypto.dylib in Frameworks */,
|
||||
@@ -342,6 +331,17 @@
|
||||
name = sockets;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B62A5A6A147748B2005F9EB0 /* processors */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B62A5A6B147748B2005F9EB0 /* hybi.hpp */,
|
||||
B62A5A6C147748B2005F9EB0 /* hybi_legacy.hpp */,
|
||||
B62A5A6D147748B2005F9EB0 /* processor.hpp */,
|
||||
);
|
||||
name = processors;
|
||||
path = src/processors;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6CF18121437C370009295BE /* echo_client */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -396,13 +396,15 @@
|
||||
B6FE8D30146C721200B32547 /* hybi_processor.hpp */,
|
||||
B6FE8D2E146C2C9500B32547 /* hybi_legacy_processor.hpp */,
|
||||
B62A5A2F1469512A005F9EB0 /* interfaces */,
|
||||
B6FE8D1414686A6D00B32547 /* md5 */,
|
||||
B61387B51462B34400ED9B19 /* logger */,
|
||||
B61387A4145D847A00ED9B19 /* http */,
|
||||
B6FE8D09145B0F7400B32547 /* rng */,
|
||||
B6FE8D05145AFF5F00B32547 /* websocket_constants.hpp */,
|
||||
B62A5A6A147748B2005F9EB0 /* processors */,
|
||||
B6FE8D4914730AA600B32547 /* policy.cpp */,
|
||||
B62A5A71147759EA005F9EB0 /* common.hpp */,
|
||||
B62A5A511473EBB0005F9EB0 /* connection.hpp */,
|
||||
B62A5A6F14774CD5005F9EB0 /* uri.hpp */,
|
||||
B62A5A521473EBB0005F9EB0 /* endpoint.hpp */,
|
||||
B6DF1C921434AC470029A1B1 /* websocket_client_session.cpp */,
|
||||
B6DF1C931434AC470029A1B1 /* websocket_client_session.hpp */,
|
||||
@@ -420,6 +422,7 @@
|
||||
B6DF1C9D1434AC470029A1B1 /* websocket_session.cpp */,
|
||||
B6DF1C9E1434AC470029A1B1 /* websocket_session.hpp */,
|
||||
B6DF1C9F1434AC470029A1B1 /* websocketpp.hpp */,
|
||||
B6FE8D1414686A6D00B32547 /* md5 */,
|
||||
B6DF1C8E1434AC3E0029A1B1 /* utf8_validator */,
|
||||
B6DF1C871434ABF30029A1B1 /* sha1 */,
|
||||
B6DF1C801434ABE20029A1B1 /* base64 */,
|
||||
@@ -538,6 +541,7 @@
|
||||
children = (
|
||||
B6FE8D1614686A8500B32547 /* md5.c */,
|
||||
B6FE8D1714686A8500B32547 /* md5.h */,
|
||||
B62A5A7014774F08005F9EB0 /* md5.hpp */,
|
||||
);
|
||||
name = md5;
|
||||
sourceTree = "<group>";
|
||||
@@ -762,12 +766,6 @@
|
||||
B6DF1C7A1434AB740029A1B1 /* network_utilities.cpp in Sources */,
|
||||
B6DF1C831434ABE20029A1B1 /* base64.cpp in Sources */,
|
||||
B6DF1C8A1434AC330029A1B1 /* sha1.cpp in Sources */,
|
||||
B6DF1CA01434AC470029A1B1 /* websocket_client_session.cpp in Sources */,
|
||||
B6DF1CA41434AC470029A1B1 /* websocket_client.cpp in Sources */,
|
||||
B6DF1CAA1434AC470029A1B1 /* websocket_frame.cpp in Sources */,
|
||||
B6DF1CAE1434AC470029A1B1 /* websocket_server_session.cpp in Sources */,
|
||||
B6DF1CB21434AC470029A1B1 /* websocket_server.cpp in Sources */,
|
||||
B6DF1CB61434AC470029A1B1 /* websocket_session.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -779,9 +777,6 @@
|
||||
B6DF1CC41434AF9E0029A1B1 /* network_utilities.cpp in Sources */,
|
||||
B6DF1C841434ABE20029A1B1 /* base64.cpp in Sources */,
|
||||
B6DF1C8B1434AC330029A1B1 /* sha1.cpp in Sources */,
|
||||
B6DF1CAB1434AC470029A1B1 /* websocket_frame.cpp in Sources */,
|
||||
B6DF1CB31434AC470029A1B1 /* websocket_server.cpp in Sources */,
|
||||
B6DF1CB71434AC470029A1B1 /* websocket_session.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user