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:
Peter Thorson
2011-11-19 00:52:38 -06:00
parent 50efb8f996
commit 221693f975
18 changed files with 1226 additions and 688 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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
View 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

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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),

View File

@@ -40,6 +40,8 @@
#include "sha1.h"
using websocketpp::SHA1;
/*
* SHA1
*

View File

@@ -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_

View File

@@ -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;

View File

@@ -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
View 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

View File

@@ -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;
};
}
}

View File

@@ -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;
};