mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
more work, compiles and passes tests now
This commit is contained in:
5
Makefile
5
Makefile
@@ -28,10 +28,9 @@
|
||||
# It's authors were Jonathan Wallace and Bernhard Fluehmann.
|
||||
|
||||
|
||||
objects = websocket_server_session.o websocket_client_session.o websocket_session.o websocket_server.o websocket_client.o websocket_frame.o \
|
||||
network_utilities.o sha1.o base64.o
|
||||
objects = network_utilities.o sha1.o base64.o
|
||||
|
||||
libs = -lboost_system -lboost_date_time -lboost_regex -lboost_random
|
||||
libs = -lboost_system -lboost_date_time -lboost_regex -lboost_random -lboost_program_options
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ CXX ?= c++
|
||||
SHARED ?= "1"
|
||||
|
||||
ifeq ($(SHARED), 1)
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lwebsocketpp
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lboost_program_options -lwebsocketpp
|
||||
else
|
||||
LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lboost_regex -lboost_random -lboost_program_options ../../libwebsocketpp.a
|
||||
endif
|
||||
|
||||
@@ -25,17 +25,3 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "echo.hpp"
|
||||
|
||||
using websocketecho::echo_server_handler;
|
||||
|
||||
void echo_server_handler::validate(websocketpp::session_ptr client) {}
|
||||
|
||||
void echo_server_handler::on_message(websocketpp::session_ptr client, const std::string &msg) {
|
||||
client->send(msg);
|
||||
}
|
||||
|
||||
void echo_server_handler::on_message(websocketpp::session_ptr client,
|
||||
const std::vector<unsigned char> &data) {
|
||||
client->send(data);
|
||||
}
|
||||
|
||||
@@ -34,17 +34,20 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using websocketpp::session_ptr;
|
||||
|
||||
namespace websocketecho {
|
||||
|
||||
class echo_server_handler : public websocketpp::connection_handler {
|
||||
template <typename session_type>
|
||||
class echo_server_handler : public websocketpp::connection_handler<session_type> {
|
||||
public:
|
||||
//typedef boost::shared_ptr<echo_server_handler<session_type> >
|
||||
//typedef typename websocketpp::connection_handler<session_type>::ptr session_ptr;
|
||||
typedef typename websocketpp::connection_handler<session_type>::session_ptr session_ptr;
|
||||
|
||||
echo_server_handler() {}
|
||||
virtual ~echo_server_handler() {}
|
||||
|
||||
// The echo server allows all domains is protocol free.
|
||||
void validate(session_ptr client);
|
||||
void validate(session_ptr client) {}
|
||||
|
||||
// an echo server is stateless.
|
||||
// The handler has no need to keep track of connected clients.
|
||||
@@ -53,13 +56,15 @@ public:
|
||||
void on_close(session_ptr client) {}
|
||||
|
||||
// both text and binary messages are echoed back to the sending client.
|
||||
void on_message(session_ptr client,const std::string &msg);
|
||||
void on_message(session_ptr client,const std::string &msg) {
|
||||
client->send(msg);
|
||||
}
|
||||
void on_message(session_ptr client,
|
||||
const std::vector<unsigned char> &data);
|
||||
const std::vector<unsigned char> &data) {
|
||||
client->send(data);
|
||||
}
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<echo_server_handler> echo_server_handler_ptr;
|
||||
|
||||
}
|
||||
|
||||
#endif // ECHO_SERVER_HANDLER_HPP
|
||||
|
||||
@@ -50,15 +50,22 @@ int main(int argc, char* argv[]) {
|
||||
temp << host << ":" << port;
|
||||
full_host = temp.str();
|
||||
|
||||
websocketecho::echo_server_handler_ptr echo_handler(new websocketecho::echo_server_handler());
|
||||
|
||||
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
tcp::endpoint endpoint(tcp::v6(), port);
|
||||
|
||||
websocketpp::server_ptr server(
|
||||
new websocketpp::server<>(io_service,endpoint,echo_handler)
|
||||
);
|
||||
using websocketpp::server;
|
||||
|
||||
typedef boost::shared_ptr< server<> > server_ptr;
|
||||
//typedef server<>::ptr server_ptr;
|
||||
|
||||
server_ptr s(new server<>(io_service,endpoint));
|
||||
|
||||
server<>::connection_handler_ptr handler = s->make_handler<websocketecho::echo_server_handler>();
|
||||
|
||||
s->set_default_connection_handler(handler);
|
||||
|
||||
//server->parse_command_line(argc, argv);
|
||||
|
||||
@@ -68,10 +75,10 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// bump up max message size to maximum since we may be using the echo
|
||||
// server to test performance and protocol extremes.
|
||||
server->set_max_message_size(websocketpp::frame::PAYLOAD_64BIT_LIMIT);
|
||||
s->set_max_message_size(websocketpp::frame::limits::PAYLOAD_SIZE_JUMBO);
|
||||
|
||||
// start the server
|
||||
server->start_accept();
|
||||
s->start_accept();
|
||||
|
||||
std::cout << "Starting echo server on " << full_host << std::endl;
|
||||
|
||||
|
||||
@@ -26,13 +26,3 @@
|
||||
* This Makefile was derived from a similar one included in the libjson project
|
||||
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
|
||||
*/
|
||||
|
||||
#include "blank_rng.hpp"
|
||||
|
||||
using websocketpp::blank_rng;
|
||||
|
||||
blank_rng::blank_rng() {}
|
||||
|
||||
int32_t blank_rng::gen() {
|
||||
throw "Random Number generation not supported";
|
||||
}
|
||||
@@ -36,8 +36,9 @@ namespace websocketpp {
|
||||
|
||||
class blank_rng {
|
||||
public:
|
||||
blank_rng();
|
||||
int32_t gen();
|
||||
int32_t gen() {
|
||||
throw "Random Number generation not supported";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -33,17 +33,16 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace websocketpp {
|
||||
class connection_handler;
|
||||
typedef boost::shared_ptr<connection_handler> connection_handler_ptr;
|
||||
}
|
||||
|
||||
#include "websocket_session.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
template <typename session_type>
|
||||
class connection_handler {
|
||||
public:
|
||||
typedef connection_handler<session_type> connection_handler_type;
|
||||
|
||||
typedef boost::shared_ptr<connection_handler_type> ptr;
|
||||
typedef boost::shared_ptr<session_type> session_ptr;
|
||||
|
||||
// validate will be called after a websocket handshake has been received and
|
||||
// before it is accepted. It provides a handler the ability to refuse a
|
||||
// connection based on application specific logic (ex: restrict domains or
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// for exceptions that should be somewhere else
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
// Defaults
|
||||
namespace websocketpp {
|
||||
const uint64_t DEFAULT_MAX_MESSAGE_SIZE = 0xFFFFFF; // ~16MB
|
||||
@@ -151,6 +155,20 @@ namespace websocketpp {
|
||||
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
|
||||
|
||||
@@ -534,7 +534,10 @@ public:
|
||||
|
||||
if (payload_size > max_payload_size) {
|
||||
// TODO: frame/message size limits
|
||||
throw websocketpp::server_error("got frame with payload greater than maximum frame buffer size.");
|
||||
// TODO: find a way to throw a server error without coupling frame
|
||||
// with server
|
||||
// throw websocketpp::server_error("got frame with payload greater than maximum frame buffer size.");
|
||||
throw "Got frame with payload greater than maximum frame buffer size.";
|
||||
}
|
||||
m_payload.resize(payload_size);
|
||||
m_bytes_needed = payload_size;
|
||||
|
||||
@@ -36,11 +36,6 @@ namespace po = boost::program_options;
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace websocketpp {
|
||||
//class server;
|
||||
//typedef boost::shared_ptr<server> server_ptr;
|
||||
}
|
||||
|
||||
#include "websocketpp.hpp"
|
||||
#include "websocket_session.hpp"
|
||||
#include "websocket_connection_handler.hpp"
|
||||
@@ -53,34 +48,28 @@ using boost::asio::ip::tcp;
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template <class rng_policy = blank_rng>
|
||||
|
||||
template <typename rng_policy = blank_rng>
|
||||
class server : public boost::enable_shared_from_this< server<rng_policy> > {
|
||||
public:
|
||||
typedef boost::shared_ptr< server<rng_policy> > ptr;
|
||||
typedef rng_policy rng_t;
|
||||
|
||||
typedef server<rng_policy> server_type;
|
||||
typedef session<server_type> session_type;
|
||||
typedef connection_handler<session_type> connection_handler_type;
|
||||
|
||||
typedef boost::shared_ptr<server_type> ptr;
|
||||
typedef boost::shared_ptr<session_type> session_ptr;
|
||||
typedef boost::shared_ptr<connection_handler_type> connection_handler_ptr;
|
||||
|
||||
server<rng_policy>(boost::asio::io_service& io_service,
|
||||
const tcp::endpoint& endpoint,
|
||||
connection_handler_ptr defc)
|
||||
const tcp::endpoint& endpoint)
|
||||
: m_elog_level(LOG_ALL),
|
||||
m_alog_level(ALOG_ALL),
|
||||
m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE),
|
||||
m_io_service(io_service),
|
||||
m_acceptor(io_service, endpoint),
|
||||
m_def_con_handler(defc),
|
||||
m_desc("websocketpp::server")
|
||||
{
|
||||
m_desc.add_options()
|
||||
@@ -90,13 +79,21 @@ public:
|
||||
;
|
||||
}
|
||||
|
||||
void set_default_connection_handler(connection_handler_ptr c) {
|
||||
m_def_con_handler = c;
|
||||
}
|
||||
|
||||
// creates a new session object and connects the next websocket
|
||||
// connection to it.
|
||||
void start_accept() {
|
||||
if (m_def_con_handler == connection_handler_ptr()) {
|
||||
throw server_error("start_accept called before a connection handler was set");
|
||||
}
|
||||
|
||||
// TODO: sanity check whether the session buffer size bound could be reduced
|
||||
session<server<rng_policy> >::ptr new_session(
|
||||
new session<server<rng_policy> >(
|
||||
shared_from_this(),
|
||||
session_ptr new_session(
|
||||
new session_type(
|
||||
server_type::shared_from_this(),
|
||||
m_io_service,
|
||||
m_def_con_handler,
|
||||
m_max_message_size*2
|
||||
@@ -106,26 +103,20 @@ public:
|
||||
m_acceptor.async_accept(
|
||||
new_session->socket(),
|
||||
boost::bind(
|
||||
&server::handle_accept,
|
||||
shared_from_this(),
|
||||
&server_type::handle_accept,
|
||||
server_type::shared_from_this(),
|
||||
new_session,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// INTERFACE FOR LOCAL APPLICATIONS
|
||||
|
||||
// Add or remove a host string (host:port) to the list of acceptable
|
||||
// hosts to accept websocket connections from. Additions/deletions here
|
||||
// only affect new connections.
|
||||
void add_host(std::string host) {
|
||||
m_hosts.insert(host);
|
||||
}
|
||||
void remove_host(std::string host) {
|
||||
m_hosts.erase(host);
|
||||
template <template <class> class T>
|
||||
connection_handler_ptr make_handler() {
|
||||
return boost::shared_ptr< T<session_type> >(new T<session_type>());
|
||||
}
|
||||
|
||||
// INTERFACE FOR LOCAL APPLICATIONS
|
||||
void set_max_message_size(uint64_t val) {
|
||||
if (val > frame::limits::PAYLOAD_SIZE_JUMBO) {
|
||||
std::stringstream err;
|
||||
@@ -205,15 +196,25 @@ public:
|
||||
|
||||
// INTERFACE FOR SESSIONS
|
||||
|
||||
static const bool is_server = true;
|
||||
|
||||
rng_policy& get_rng() {
|
||||
return &m_rng;
|
||||
return m_rng;
|
||||
}
|
||||
|
||||
// Check if this server will respond to this host.
|
||||
// Confirms that the port in the host string matches the port we are listening
|
||||
// on. End user application is responsible for checking the /host/ part.
|
||||
bool validate_host(std::string host) {
|
||||
if (m_hosts.find(host) == m_hosts.end()) {
|
||||
return false;
|
||||
}
|
||||
// find colon.
|
||||
// if no colon assume default port
|
||||
|
||||
// if port == port
|
||||
// return true
|
||||
// else
|
||||
// return false
|
||||
|
||||
// TODO: just check the port. Otherwise user is responsible for checking this
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -247,8 +248,7 @@ public:
|
||||
private:
|
||||
// if no errors starts the session's read loop and returns to the
|
||||
// start_accept phase.
|
||||
void handle_accept(server_session_ptr session,
|
||||
const boost::system::error_code& error)
|
||||
void handle_accept(session_ptr session,const boost::system::error_code& error)
|
||||
{
|
||||
if (!error) {
|
||||
session->on_connect();
|
||||
@@ -266,8 +266,9 @@ private:
|
||||
private:
|
||||
uint16_t m_elog_level;
|
||||
uint16_t m_alog_level;
|
||||
|
||||
std::set<std::string> m_hosts;
|
||||
|
||||
std::vector<session_ptr> m_sessions;
|
||||
|
||||
uint64_t m_max_message_size;
|
||||
boost::asio::io_service& m_io_service;
|
||||
tcp::acceptor m_acceptor;
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace websocketpp {
|
||||
|
||||
#include "websocketpp.hpp"
|
||||
#include "websocket_frame.hpp"
|
||||
#include "websocket_server.hpp" // for server error?
|
||||
#include "websocket_connection_handler.hpp"
|
||||
|
||||
#include "base64/base64.h"
|
||||
@@ -122,19 +123,42 @@ namespace state {
|
||||
CLOSED = 3
|
||||
};
|
||||
}
|
||||
|
||||
// Exception classes
|
||||
|
||||
class handshake_error : public std::exception {
|
||||
public:
|
||||
handshake_error(const std::string& msg,
|
||||
int http_error,
|
||||
const std::string& http_msg = "")
|
||||
: m_msg(msg),m_http_error_code(http_error),m_http_error_msg(http_msg) {}
|
||||
~handshake_error() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
std::string m_msg;
|
||||
int m_http_error_code;
|
||||
std::string m_http_error_msg;
|
||||
};
|
||||
|
||||
typedef std::map<std::string,std::string> header_list;
|
||||
|
||||
template <typename endpoint_policy>
|
||||
class session : public boost::enable_shared_from_this<session<endpoint_policy> > {
|
||||
class session : public boost::enable_shared_from_this< session<endpoint_policy> > {
|
||||
public:
|
||||
typedef endpoint_policy endpoint_t;
|
||||
typedef boost::shared_ptr<session<endpoint_policy> > ptr;
|
||||
typedef endpoint_policy endpoint_type;
|
||||
typedef session<endpoint_policy> session_type;
|
||||
typedef connection_handler<session_type> connection_handler_type;
|
||||
|
||||
typedef boost::shared_ptr<endpoint_type> endpoint_ptr;
|
||||
typedef boost::shared_ptr<session_type> ptr;
|
||||
typedef boost::shared_ptr<connection_handler_type> connection_handler_ptr;
|
||||
|
||||
friend class handshake_error;
|
||||
|
||||
session (typename endpoint_policy::ptr e,
|
||||
session (endpoint_ptr e,
|
||||
boost::asio::io_service& io_service,
|
||||
connection_handler_ptr defc,
|
||||
uint64_t buf_size)
|
||||
@@ -147,6 +171,7 @@ public:
|
||||
m_dropped_by_me(false),
|
||||
m_socket(io_service),
|
||||
m_io_service(io_service),
|
||||
m_endpoint(e),
|
||||
m_local_interface(defc),
|
||||
m_timer(io_service,boost::posix_time::seconds(0)),
|
||||
m_buf(buf_size), // maximum buffered (unconsumed) bytes from network
|
||||
@@ -171,7 +196,10 @@ public:
|
||||
// that come after it are called as a result of an async event completing.
|
||||
// if any method in this chain returns before adding a new async event the
|
||||
// session will end.
|
||||
virtual void on_connect() = 0;
|
||||
// TODO: this needs to be a template specialization or member of the endpoint
|
||||
void on_connect() {
|
||||
read_handshake();
|
||||
}
|
||||
|
||||
// sets the internal connection handler of this connection to new_con.
|
||||
// This is useful if you want to switch handler objects during a connection
|
||||
@@ -184,7 +212,7 @@ public:
|
||||
//m_local_interface->disconnect(shared_from_this(),4000,"Setting new connection handler");
|
||||
}
|
||||
m_local_interface = new_con;
|
||||
m_local_interface->on_open(shared_from_this());
|
||||
m_local_interface->on_open(session_type::shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +227,9 @@ public:
|
||||
const std::string& get_subprotocol() const {
|
||||
if (m_state == state::CONNECTING) {
|
||||
log("Subprotocol is not avaliable before the handshake has completed.",LOG_WARN);
|
||||
throw server_error("Subprotocol is not avaliable before the handshake has completed.");
|
||||
// TODO: fix server_error
|
||||
//throw server_error("Subprotocol is not avaliable before the handshake has completed.");
|
||||
throw "Subprotocol is not avaliable before the handshake has completed";
|
||||
}
|
||||
return m_server_subprotocol;
|
||||
}
|
||||
@@ -223,6 +253,48 @@ public:
|
||||
return m_version;
|
||||
}
|
||||
|
||||
|
||||
/**** TODO: SERVER SPECIFIC ****/
|
||||
|
||||
void set_header(const std::string &key,const std::string &val) {
|
||||
// TODO: prevent use of reserved headers;
|
||||
m_server_headers[key] = val;
|
||||
}
|
||||
|
||||
void select_subprotocol(const std::string& val) {
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_client_subprotocols.begin(),
|
||||
m_client_subprotocols.end(),
|
||||
val);
|
||||
|
||||
if (val != "" && it == m_client_subprotocols.end()) {
|
||||
throw server_error("Attempted to choose a subprotocol not proposed by the client");
|
||||
}
|
||||
|
||||
m_server_subprotocol = val;
|
||||
}
|
||||
|
||||
void select_extension(const std::string& val) {
|
||||
if (val == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_client_extensions.begin(),
|
||||
m_client_extensions.end(),
|
||||
val);
|
||||
|
||||
if (it == m_client_extensions.end()) {
|
||||
throw server_error("Attempted to choose an extension not proposed by the client");
|
||||
}
|
||||
|
||||
m_server_extensions.push_back(val);
|
||||
}
|
||||
|
||||
/********/
|
||||
|
||||
/*** SESSION INTERFACE ***/
|
||||
|
||||
// send basic frame types
|
||||
@@ -278,16 +350,279 @@ public:
|
||||
send_close(status,reason);
|
||||
}
|
||||
|
||||
virtual bool is_server() const = 0;
|
||||
bool is_server() const {
|
||||
return endpoint_type::is_server;
|
||||
}
|
||||
|
||||
// Opening handshake processors and callbacks. These need to be defined in
|
||||
// derived classes.
|
||||
virtual void handle_write_handshake(const boost::system::error_code& e) = 0;
|
||||
virtual void handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred) = 0;
|
||||
|
||||
// TODO: endpoint specific
|
||||
//virtual void handle_write_handshake(const boost::system::error_code& e) = 0;
|
||||
void handle_write_handshake(const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
log_error("Error writing handshake response",error);
|
||||
drop_tcp();
|
||||
return;
|
||||
}
|
||||
|
||||
log_open_result();
|
||||
|
||||
if (m_server_http_code != 101) {
|
||||
std::stringstream err;
|
||||
err << "Handshake ended with HTTP error: " << m_server_http_code << " "
|
||||
<< (m_server_http_string != "" ? m_server_http_string : lookup_http_error_string(m_server_http_code));
|
||||
log(err.str(),LOG_ERROR);
|
||||
drop_tcp();
|
||||
// TODO: tell client that connection failed.
|
||||
return;
|
||||
}
|
||||
|
||||
m_state = state::OPEN;
|
||||
|
||||
// stop the handshake timer
|
||||
m_timer.cancel();
|
||||
|
||||
if (m_local_interface) {
|
||||
m_local_interface->on_open(session_type::shared_from_this());
|
||||
}
|
||||
|
||||
reset_message();
|
||||
this->read_frame();
|
||||
}
|
||||
|
||||
// TODO: endpoint specific
|
||||
//virtual void handle_read_handshake(const boost::system::error_code& e,std::size_t bytes_transferred) = 0;
|
||||
void handle_read_handshake(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred) {
|
||||
std::ostringstream line;
|
||||
line << &m_buf;
|
||||
m_raw_client_handshake += line.str();
|
||||
|
||||
access_log(m_raw_client_handshake,ALOG_HANDSHAKE);
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type end;
|
||||
|
||||
// Get request and parse headers
|
||||
end = m_raw_client_handshake.find("\r\n",start);
|
||||
|
||||
while(end != std::string::npos) {
|
||||
tokens.push_back(m_raw_client_handshake.substr(start, end - start));
|
||||
|
||||
start = end + 2;
|
||||
|
||||
end = m_raw_client_handshake.find("\r\n",start);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tokens.size(); i++) {
|
||||
if (i == 0) {
|
||||
m_client_http_request = tokens[i];
|
||||
}
|
||||
|
||||
end = tokens[i].find(": ",0);
|
||||
|
||||
if (end != std::string::npos) {
|
||||
std::string h = tokens[i].substr(0,end);
|
||||
|
||||
if (get_client_header(h) == "") {
|
||||
m_client_headers[h] = tokens[i].substr(end+2);
|
||||
} else {
|
||||
m_client_headers[h] += ", " + tokens[i].substr(end+2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handshake error checking
|
||||
try {
|
||||
std::stringstream err;
|
||||
std::string h;
|
||||
|
||||
// check the method
|
||||
if (m_client_http_request.substr(0,4) != "GET ") {
|
||||
err << "Websocket handshake has invalid method: "
|
||||
<< m_client_http_request.substr(0,4);
|
||||
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
// check the HTTP version
|
||||
// TODO: allow versions greater than 1.1
|
||||
end = m_client_http_request.find(" HTTP/1.1",4);
|
||||
if (end == std::string::npos) {
|
||||
err << "Websocket handshake has invalid HTTP version";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
m_resource = m_client_http_request.substr(4,end-4);
|
||||
|
||||
// verify the presence of required headers
|
||||
h = get_client_header("Host");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Host header is missing",400));
|
||||
} else if (!m_endpoint->validate_host(h)) {
|
||||
err << "Host " << h << " is not one of this server's names.";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
h = get_client_header("Upgrade");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Upgrade header is missing",400));
|
||||
} else if (!boost::iequals(h,"websocket")) {
|
||||
err << "Upgrade header was " << h << " instead of \"websocket\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
h = get_client_header("Connection");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Connection header is missing",400));
|
||||
} else if (!boost::ifind_first(h,"upgrade")) {
|
||||
err << "Connection header, \"" << h
|
||||
<< "\", does not contain required token \"upgrade\"";
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
|
||||
if (get_client_header("Sec-WebSocket-Key") == "") {
|
||||
throw(handshake_error("Required Sec-WebSocket-Key header is missing",400));
|
||||
}
|
||||
|
||||
h = get_client_header("Sec-WebSocket-Version");
|
||||
if (h == "") {
|
||||
throw(handshake_error("Required Sec-WebSocket-Version header is missing",400));
|
||||
} else {
|
||||
m_version = atoi(h.c_str());
|
||||
|
||||
if (m_version != 7 && m_version != 8 && m_version != 13) {
|
||||
err << "This server doesn't support WebSocket protocol version "
|
||||
<< m_version;
|
||||
throw(handshake_error(err.str(),400));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_version < 13) {
|
||||
h = get_client_header("Sec-WebSocket-Origin");
|
||||
} else {
|
||||
h = get_client_header("Origin");
|
||||
}
|
||||
|
||||
if (h != "") {
|
||||
m_client_origin = h;
|
||||
}
|
||||
|
||||
// TODO: extract subprotocols
|
||||
// TODO: extract extensions
|
||||
|
||||
// optional headers (delegated to the local interface)
|
||||
if (m_local_interface) {
|
||||
m_local_interface->validate(session_type::shared_from_this());
|
||||
}
|
||||
|
||||
m_server_http_code = 101;
|
||||
m_server_http_string = "Switching Protocols";
|
||||
} catch (const handshake_error& e) {
|
||||
std::stringstream err;
|
||||
err << "Caught handshake exception: " << e.what();
|
||||
|
||||
access_log(e.what(),ALOG_HANDSHAKE);
|
||||
log(err.str(),LOG_ERROR);
|
||||
|
||||
m_server_http_code = e.m_http_error_code;
|
||||
m_server_http_string = e.m_http_error_msg;
|
||||
}
|
||||
|
||||
write_handshake();
|
||||
}
|
||||
public: //protected:
|
||||
virtual void write_handshake() = 0;
|
||||
virtual void read_handshake() = 0;
|
||||
// TODO: endpoint specific
|
||||
void write_handshake() {
|
||||
std::stringstream h;
|
||||
|
||||
if (m_server_http_code == 101) {
|
||||
std::string server_key = get_client_header("Sec-WebSocket-Key");
|
||||
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
SHA1 sha;
|
||||
uint32_t message_digest[5];
|
||||
|
||||
sha.Reset();
|
||||
sha << server_key.c_str();
|
||||
|
||||
if (sha.Result(message_digest)){
|
||||
// convert sha1 hash bytes to network byte order because this sha1
|
||||
// library works on ints rather than bytes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
message_digest[i] = htonl(message_digest[i]);
|
||||
}
|
||||
|
||||
server_key = base64_encode(
|
||||
reinterpret_cast<const unsigned char*>(message_digest),20);
|
||||
|
||||
// set handshake accept headers
|
||||
set_header("Sec-WebSocket-Accept",server_key);
|
||||
set_header("Upgrade","websocket");
|
||||
set_header("Connection","Upgrade");
|
||||
} else {
|
||||
log("Error computing handshake sha1 hash.",LOG_ERROR);
|
||||
m_server_http_code = 500;
|
||||
m_server_http_string = "";
|
||||
}
|
||||
}
|
||||
|
||||
// hardcoded server headers
|
||||
set_header("Server","WebSocket++/2011-09-25");
|
||||
|
||||
h << "HTTP/1.1 " << m_server_http_code << " "
|
||||
<< (m_server_http_string != "" ? m_server_http_string :
|
||||
lookup_http_error_string(m_server_http_code))
|
||||
<< "\r\n";
|
||||
|
||||
header_list::iterator it;
|
||||
for (it = m_server_headers.begin(); it != m_server_headers.end(); it++) {
|
||||
h << it->first << ": " << it->second << "\r\n";
|
||||
}
|
||||
|
||||
h << "\r\n";
|
||||
|
||||
m_raw_server_handshake = h.str();
|
||||
|
||||
// start async write to handle_write_handshake
|
||||
boost::asio::async_write(
|
||||
m_socket,
|
||||
boost::asio::buffer(m_raw_server_handshake),
|
||||
boost::bind(
|
||||
&session_type::handle_write_handshake,
|
||||
session_type::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: endpoint specific
|
||||
//virtual void read_handshake() = 0;
|
||||
void read_handshake() {
|
||||
m_timer.expires_from_now(boost::posix_time::seconds(5));
|
||||
|
||||
m_timer.async_wait(
|
||||
boost::bind(
|
||||
&session_type::handle_handshake_expired,
|
||||
session_type::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
|
||||
boost::asio::async_read_until(
|
||||
m_socket,
|
||||
m_buf,
|
||||
"\r\n\r\n",
|
||||
boost::bind(
|
||||
&session_type::handle_read_handshake,
|
||||
session_type::shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void read_frame() {
|
||||
// the initial read in the handshake may have read in the first frame.
|
||||
@@ -335,13 +670,13 @@ public: //protected:
|
||||
|
||||
std::stringstream err;
|
||||
|
||||
err << "consuming. have: " << m_buf.size() << " bytes. Need: " << m_read_frame.get_bytes_needed() << " state: " << (int)m_read_frame.get_state();
|
||||
log(err.str(),LOG_DEBUG);
|
||||
//err << "consuming. have: " << m_buf.size() << " bytes. Need: " << m_read_frame.get_bytes_needed() << " state: " << (int)m_read_frame.get_state();
|
||||
//log(err.str(),LOG_DEBUG);
|
||||
m_read_frame.consume(s);
|
||||
|
||||
err.str("");
|
||||
err << "consume complete, " << m_buf.size() << " bytes left, " << m_read_frame.get_bytes_needed() << " still needed, state: " << (int)m_read_frame.get_state();
|
||||
log(err.str(),LOG_DEBUG);
|
||||
//err.str("");
|
||||
//err << "consume complete, " << m_buf.size() << " bytes left, " << m_read_frame.get_bytes_needed() << " still needed, state: " << (int)m_read_frame.get_state();
|
||||
//log(err.str(),LOG_DEBUG);
|
||||
|
||||
if (m_read_frame.ready()) {
|
||||
// process frame and reset frame state for the next frame.
|
||||
@@ -408,8 +743,8 @@ public: //protected:
|
||||
m_buf,
|
||||
boost::asio::transfer_at_least(m_read_frame.get_bytes_needed()),
|
||||
boost::bind(
|
||||
&session::handle_read_frame,
|
||||
shared_from_this(),
|
||||
&session<endpoint_policy>::handle_read_frame,
|
||||
session<endpoint_policy>::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
@@ -418,7 +753,7 @@ public: //protected:
|
||||
|
||||
if (m_local_interface) {
|
||||
// TODO: make sure close code/msg are properly set.
|
||||
m_local_interface->on_close(shared_from_this());
|
||||
m_local_interface->on_close(session_type::shared_from_this());
|
||||
}
|
||||
|
||||
m_timer.cancel();
|
||||
@@ -455,8 +790,8 @@ public: //protected:
|
||||
m_socket,
|
||||
data,
|
||||
boost::bind(
|
||||
&session::handle_write_frame,
|
||||
shared_from_this(),
|
||||
&session<endpoint_policy>::handle_write_frame,
|
||||
session<endpoint_policy>::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
@@ -671,9 +1006,9 @@ public: //protected:
|
||||
if (m_current_opcode == frame::opcode::BINARY) {
|
||||
//log("Dispatching Binary Message",LOG_DEBUG);
|
||||
if (m_fragmented) {
|
||||
m_local_interface->on_message(shared_from_this(),m_current_message);
|
||||
m_local_interface->on_message(session_type::shared_from_this(),m_current_message);
|
||||
} else {
|
||||
m_local_interface->on_message(shared_from_this(),
|
||||
m_local_interface->on_message(session_type::shared_from_this(),
|
||||
m_read_frame.get_payload());
|
||||
}
|
||||
} else if (m_current_opcode == frame::opcode::TEXT) {
|
||||
@@ -698,7 +1033,7 @@ public: //protected:
|
||||
}
|
||||
|
||||
//log("Dispatching Text Message",LOG_DEBUG);
|
||||
m_local_interface->on_message(shared_from_this(),msg);
|
||||
m_local_interface->on_message(session_type::shared_from_this(),msg);
|
||||
} else {
|
||||
// Not sure if this should be a fatal error or not
|
||||
std::stringstream err;
|
||||
@@ -727,8 +1062,13 @@ public: //protected:
|
||||
}
|
||||
|
||||
// logging
|
||||
virtual void log(const std::string& msg, uint16_t level) const = 0;
|
||||
virtual void access_log(const std::string& msg, uint16_t level) const = 0;
|
||||
// TODO: endpoint specific
|
||||
void log(const std::string& msg, uint16_t level) const {
|
||||
m_endpoint->log(msg,level);
|
||||
}
|
||||
void access_log(const std::string& msg, uint16_t level) const {
|
||||
m_endpoint->access_log(msg,level);
|
||||
}
|
||||
|
||||
void log_close_result() {
|
||||
std::stringstream msg;
|
||||
@@ -788,8 +1128,8 @@ public: //protected:
|
||||
|
||||
m_timer.async_wait(
|
||||
boost::bind(
|
||||
&session::handle_close_expired,
|
||||
shared_from_this(),
|
||||
&session<endpoint_policy>::handle_close_expired,
|
||||
session<endpoint_policy>::shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
@@ -884,6 +1224,7 @@ protected:
|
||||
// Connection Resources
|
||||
tcp::socket m_socket;
|
||||
boost::asio::io_service& m_io_service;
|
||||
endpoint_ptr m_endpoint;
|
||||
connection_handler_ptr m_local_interface;
|
||||
boost::asio::deadline_timer m_timer;
|
||||
|
||||
@@ -905,24 +1246,7 @@ protected:
|
||||
bool m_error;
|
||||
};
|
||||
|
||||
// Exception classes
|
||||
|
||||
class handshake_error : public std::exception {
|
||||
public:
|
||||
handshake_error(const std::string& msg,
|
||||
int http_error,
|
||||
const std::string& http_msg = "")
|
||||
: m_msg(msg),m_http_error_code(http_error),m_http_error_msg(http_msg) {}
|
||||
~handshake_error() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
std::string m_msg;
|
||||
int m_http_error_code;
|
||||
std::string m_http_error_msg;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#include "websocket_constants.hpp"
|
||||
|
||||
#include "websocket_session.hpp"
|
||||
//#include "websocket_session.hpp"
|
||||
//#include "websocket_server_session.hpp"
|
||||
//#include "websocket_client_session.hpp"
|
||||
#include "websocket_server.hpp"
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
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 */; };
|
||||
@@ -33,11 +35,9 @@
|
||||
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 */; };
|
||||
B6DF1CA11434AC470029A1B1 /* 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 */; };
|
||||
B6DF1CA51434AC470029A1B1 /* 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, ); }; };
|
||||
@@ -47,7 +47,6 @@
|
||||
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 */; };
|
||||
B6DF1CAF1434AC470029A1B1 /* 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 */; };
|
||||
@@ -129,6 +128,7 @@
|
||||
B6138765145AD1F700ED9B19 /* chat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat.cpp; path = examples/chat_server/chat.cpp; sourceTree = "<group>"; };
|
||||
B6138766145AD1F700ED9B19 /* chat.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = chat.hpp; path = examples/chat_server/chat.hpp; sourceTree = "<group>"; };
|
||||
B6138767145AD1F700ED9B19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; name = Makefile; path = examples/chat_server/Makefile; sourceTree = "<group>"; };
|
||||
B6138791145CA6F700ED9B19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; 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>"; };
|
||||
@@ -228,6 +228,8 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B6138790145CA61C00ED9B19 /* libboost_program_options.dylib in Frameworks */,
|
||||
B613878F145CA61300ED9B19 /* libboost_date_time.dylib in Frameworks */,
|
||||
B6DF1CE41435F8250029A1B1 /* Foundation.framework in Frameworks */,
|
||||
B6DF1CE21435F1860029A1B1 /* libboost_system.dylib in Frameworks */,
|
||||
B6DF1CDE1435EDF00029A1B1 /* libwebsocketpp.dylib in Frameworks */,
|
||||
@@ -261,6 +263,7 @@
|
||||
B6FE8CE4144DE18900B32547 /* documentation */,
|
||||
B6DF1CC61435ED380029A1B1 /* examples */,
|
||||
B6DF1CC51435ECE40029A1B1 /* libraries */,
|
||||
B6138791145CA6F700ED9B19 /* Makefile */,
|
||||
B6DF1C7F1434ABB70029A1B1 /* src */,
|
||||
B6DF1C6A1434A7A30029A1B1 /* Products */,
|
||||
);
|
||||
@@ -625,10 +628,7 @@
|
||||
B6DF1CC41434AF9E0029A1B1 /* network_utilities.cpp in Sources */,
|
||||
B6DF1C841434ABE20029A1B1 /* base64.cpp in Sources */,
|
||||
B6DF1C8B1434AC330029A1B1 /* sha1.cpp in Sources */,
|
||||
B6DF1CA11434AC470029A1B1 /* websocket_client_session.cpp in Sources */,
|
||||
B6DF1CA51434AC470029A1B1 /* websocket_client.cpp in Sources */,
|
||||
B6DF1CAB1434AC470029A1B1 /* websocket_frame.cpp in Sources */,
|
||||
B6DF1CAF1434AC470029A1B1 /* websocket_server_session.cpp in Sources */,
|
||||
B6DF1CB31434AC470029A1B1 /* websocket_server.cpp in Sources */,
|
||||
B6DF1CB71434AC470029A1B1 /* websocket_session.cpp in Sources */,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user