mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge remote-tracking branch 'upstream/0.3.x-cmake' into cmake_prebuild_system
Conflicts: websocketpp/endpoint.hpp
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -75,3 +75,5 @@ build/
|
||||
examples/wsperf/wsperf_client
|
||||
|
||||
*.out
|
||||
|
||||
*.log
|
||||
|
||||
72
SConstruct
72
SConstruct
@@ -20,6 +20,9 @@ if os.environ.has_key('LINKFLAGS'):
|
||||
## or set BOOST_INCLUDES and BOOST_LIBS if Boost comes with your OS distro e.g. and
|
||||
## needs BOOST_INCLUDES=/usr/include/boost and BOOST_LIBS=/usr/lib like Ubuntu.
|
||||
##
|
||||
if os.environ.has_key('BOOSTROOT'):
|
||||
os.environ['BOOST_ROOT'] = os.environ['BOOSTROOT']
|
||||
|
||||
if os.environ.has_key('BOOST_ROOT'):
|
||||
env['BOOST_INCLUDES'] = os.environ['BOOST_ROOT']
|
||||
env['BOOST_LIBS'] = os.path.join(os.environ['BOOST_ROOT'], 'stage', 'lib')
|
||||
@@ -56,7 +59,10 @@ if env['PLATFORM'].startswith('win'):
|
||||
'WIN32_LEAN_AND_MEAN',
|
||||
'_WIN32_WINNT=0x0600',
|
||||
'_CONSOLE',
|
||||
'_WEBSOCKETPP_CPP11_FRIEND_'])
|
||||
'BOOST_TEST_DYN_LINK',
|
||||
'NOMINMAX',
|
||||
'_WEBSOCKETPP_CPP11_MEMORY_',
|
||||
'_WEBSOCKETPP_CPP11_FUNCTIONAL_'])
|
||||
arch_flags = '/arch:SSE2'
|
||||
opt_flags = '/Ox /Oi /fp:fast'
|
||||
warn_flags = '/W3 /wd4996 /wd4995 /wd4355'
|
||||
@@ -82,12 +88,14 @@ else:
|
||||
|
||||
# Compiler specific warning flags
|
||||
if env['CXX'].startswith('g++'):
|
||||
#env.Append(CCFLAGS = ['-Wconversion'])
|
||||
env.Append(CCFLAGS = ['-Wcast-align'])
|
||||
elif env['CXX'].startswith('clang++'):
|
||||
#env.Append(CCFLAGS = ['-Wcast-align'])
|
||||
#env.Append(CCFLAGS = ['-Wglobal-constructors'])
|
||||
#env.Append(CCFLAGS = ['-Wconversion'])
|
||||
env.Append(CCFLAGS = ['-Wno-padded'])
|
||||
|
||||
|
||||
# Wpadded
|
||||
# Wsign-conversion
|
||||
|
||||
@@ -117,11 +125,11 @@ env_cpp11 = env.Clone ()
|
||||
|
||||
if env_cpp11['CXX'].startswith('g++'):
|
||||
# TODO: check g++ version
|
||||
|
||||
|
||||
# g++ STL lacks support for <regex>
|
||||
|
||||
|
||||
GCC_VERSION = commands.getoutput(env_cpp11['CXX'] + ' -dumpversion')
|
||||
|
||||
|
||||
if GCC_VERSION > "4.4.0":
|
||||
print "C++11 build environment partially enabled"
|
||||
env_cpp11.Append(WSPP_CPP11_ENABLED = "true",CXXFLAGS = ['-std=c++0x'],TOOLSET = ['g++'],CPPDEFINES = ['_WEBSOCKETPP_CPP11_STL_','_WEBSOCKETPP_NO_CPP11_REGEX_'])
|
||||
@@ -133,7 +141,7 @@ if env_cpp11['CXX'].startswith('g++'):
|
||||
elif env_cpp11['CXX'].startswith('clang++'):
|
||||
print "C++11 build environment enabled"
|
||||
env_cpp11.Append(WSPP_CPP11_ENABLED = "true",CXXFLAGS = ['-std=c++0x','-stdlib=libc++'],LINKFLAGS = ['-stdlib=libc++'],TOOLSET = ['clang++'],CPPDEFINES = ['_WEBSOCKETPP_CPP11_STL_'])
|
||||
|
||||
|
||||
# look for optional second boostroot compiled with clang's libc++ STL library
|
||||
# this prevents warnings/errors when linking code built with two different
|
||||
# incompatible STL libraries.
|
||||
@@ -146,12 +154,22 @@ elif env_cpp11['CXX'].startswith('clang++'):
|
||||
else:
|
||||
print "C++11 build environment disabled"
|
||||
|
||||
#env.Append(CPPPATH = [env['BOOST_INCLUDES']])
|
||||
env.Append(CPPFLAGS = '-isystem '+env['BOOST_INCLUDES'])
|
||||
# if the build system is known to allow the isystem modifier for library include
|
||||
# values then use it for the boost libraries. Otherwise just add them to the
|
||||
# regular CPPPATH values.
|
||||
if env['CXX'].startswith('g++') or env['CXX'].startswith('clang'):
|
||||
env.Append(CPPFLAGS = '-isystem ' + env['BOOST_INCLUDES'])
|
||||
else:
|
||||
env.Append(CPPPATH = [env['BOOST_INCLUDES']])
|
||||
env.Append(LIBPATH = [env['BOOST_LIBS']])
|
||||
|
||||
#env_cpp11.Append(CPPPATH = [env_cpp11['BOOST_INCLUDES']])
|
||||
env_cpp11.Append(CPPFLAGS = '-isystem ' + env_cpp11['BOOST_INCLUDES'])
|
||||
# if the build system is known to allow the isystem modifier for library include
|
||||
# values then use it for the boost libraries. Otherwise just add them to the
|
||||
# regular CPPPATH values.
|
||||
if env_cpp11['CXX'].startswith('g++') or env_cpp11['CXX'].startswith('clang'):
|
||||
env_cpp11.Append(CPPFLAGS = '-isystem ' + env_cpp11['BOOST_INCLUDES'])
|
||||
else:
|
||||
env_cpp11.Append(CPPPATH = [env_cpp11['BOOST_INCLUDES']])
|
||||
env_cpp11.Append(LIBPATH = [env_cpp11['BOOST_LIBS']])
|
||||
|
||||
releasedir = 'build/release/'
|
||||
@@ -170,23 +188,28 @@ Export('polyfill_libs')
|
||||
|
||||
## TARGETS:
|
||||
|
||||
# Unit tests, add test folders with SConscript files to to_test list.
|
||||
to_test = ['utility','http','logger','random','processors','message_buffer','extension','transport/iostream','transport/asio','roles','endpoint','connection'] #,'http','processors','connection'
|
||||
if not env['PLATFORM'].startswith('win'):
|
||||
# Unit tests, add test folders with SConscript files to to_test list.
|
||||
to_test = ['utility','http','logger','random','processors','message_buffer','extension','transport/iostream','transport/asio','roles','endpoint','connection'] #,'http','processors','connection'
|
||||
|
||||
for t in to_test:
|
||||
new_tests = SConscript('#/test/'+t+'/SConscript',variant_dir = testdir + t, duplicate = 0)
|
||||
for a in new_tests:
|
||||
new_alias = Alias('test', [a], a.abspath)
|
||||
AlwaysBuild(new_alias)
|
||||
for t in to_test:
|
||||
new_tests = SConscript('#/test/'+t+'/SConscript',variant_dir = testdir + t, duplicate = 0)
|
||||
for a in new_tests:
|
||||
new_alias = Alias('test', [a], a.abspath)
|
||||
AlwaysBuild(new_alias)
|
||||
|
||||
# Main test application
|
||||
#main = SConscript('#/examples/dev/SConscript',variant_dir = builddir + 'dev',duplicate = 0)
|
||||
|
||||
# testee_server
|
||||
testee_server = SConscript('#/examples/testee_server/SConscript',variant_dir = builddir + 'testee_server',duplicate = 0)
|
||||
|
||||
# echo_server
|
||||
echo_server = SConscript('#/examples/echo_server/SConscript',variant_dir = builddir + 'echo_server',duplicate = 0)
|
||||
|
||||
# echo_server_tls
|
||||
echo_server_tls = SConscript('#/examples/echo_server_tls/SConscript',variant_dir = builddir + 'echo_server_tls',duplicate = 0)
|
||||
if not env['PLATFORM'].startswith('win'):
|
||||
echo_server_tls = SConscript('#/examples/echo_server_tls/SConscript',variant_dir = builddir + 'echo_server_tls',duplicate = 0)
|
||||
|
||||
# broadcast_server
|
||||
broadcast_server = SConscript('#/examples/broadcast_server/SConscript',variant_dir = builddir + 'broadcast_server',duplicate = 0)
|
||||
@@ -194,6 +217,19 @@ broadcast_server = SConscript('#/examples/broadcast_server/SConscript',variant_d
|
||||
# echo_client
|
||||
echo_client = SConscript('#/examples/echo_client/SConscript',variant_dir = builddir + 'echo_client',duplicate = 0)
|
||||
|
||||
# subprotocol_server
|
||||
subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',variant_dir = builddir + 'subprotocol_server',duplicate = 0)
|
||||
|
||||
if not env['PLATFORM'].startswith('win'):
|
||||
# iostream_server
|
||||
iostream_server = SConscript('#/examples/iostream_server/SConscript',variant_dir = builddir + 'iostream_server',duplicate = 0)
|
||||
|
||||
# telemetry_client
|
||||
telemetry_client = SConscript('#/examples/telemetry_client/SConscript',variant_dir = builddir + 'telemetry_client',duplicate = 0)
|
||||
|
||||
# print_server
|
||||
print_server = SConscript('#/examples/print_server/SConscript',variant_dir = builddir + 'print_server',duplicate = 0)
|
||||
|
||||
#
|
||||
#wsperf = SConscript('#/examples/wsperf/SConscript',
|
||||
# variant_dir = builddir + 'wsperf',
|
||||
|
||||
@@ -11,7 +11,7 @@ using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
// pull out the type of messages sent by our config
|
||||
typedef websocketpp::config::asio::message_type::ptr message_ptr;
|
||||
typedef server::message_ptr message_ptr;
|
||||
|
||||
// Define a callback to handle incoming messages
|
||||
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
|
||||
23
examples/iostream_server/SConscript
Normal file
23
examples/iostream_server/SConscript
Normal file
@@ -0,0 +1,23 @@
|
||||
## iostream server example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is avaliable build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env_cpp11.Program('iostream_server', ["iostream_server.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','regex'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('iostream_server', ["iostream_server.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
91
examples/iostream_server/iostream_server.cpp
Normal file
91
examples/iostream_server/iostream_server.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <websocketpp/config/core.hpp>
|
||||
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::core> server;
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
// pull out the type of messages sent by our config
|
||||
typedef server::message_ptr message_ptr;
|
||||
|
||||
// Define a callback to handle incoming messages
|
||||
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
if (msg->get_opcode() == websocketpp::frame::opcode::text) {
|
||||
s->get_alog().write(websocketpp::log::alevel::app,
|
||||
"Text Message Received: "+msg->get_payload());
|
||||
} else {
|
||||
s->get_alog().write(websocketpp::log::alevel::app,
|
||||
"Binary Message Received: "+websocketpp::utility::to_hex(msg->get_payload()));
|
||||
}
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
s->get_alog().write(websocketpp::log::alevel::app,
|
||||
"Echo Failed: "+e.message());
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
server s;
|
||||
std::ofstream log;
|
||||
|
||||
try {
|
||||
// set up access channels to only log interesting things
|
||||
s.clear_access_channels(websocketpp::log::alevel::all);
|
||||
s.set_access_channels(websocketpp::log::alevel::connect);
|
||||
s.set_access_channels(websocketpp::log::alevel::disconnect);
|
||||
s.set_access_channels(websocketpp::log::alevel::app);
|
||||
|
||||
// Log to a file rather than stdout, as we are using stdout for real
|
||||
// output
|
||||
log.open("output.log");
|
||||
s.get_alog().set_ostream(&log);
|
||||
s.get_elog().set_ostream(&log);
|
||||
|
||||
// print all output to stdout
|
||||
s.register_ostream(&std::cout);
|
||||
|
||||
// Register our message handler
|
||||
s.set_message_handler(bind(&on_message,&s,::_1,::_2));
|
||||
|
||||
server::connection_ptr con = s.get_connection();
|
||||
|
||||
con->start();
|
||||
|
||||
// C++ iostream's don't support the idea of asynchronous i/o. As such
|
||||
// there are two input strategies demonstrated here. Buffered I/O will
|
||||
// read from stdin in chunks until EOF. This works very well for
|
||||
// replaying canned connections as would be done in automated testing.
|
||||
//
|
||||
// If the server is being used live however, assuming input is being
|
||||
// piped from elsewhere in realtime, this strategy will result in small
|
||||
// messages being buffered forever. The non-buffered strategy below
|
||||
// reads characters from stdin one at a time. This is inefficient and
|
||||
// for more serious uses should be replaced with a platform specific
|
||||
// asyncronous i/o technique like select, poll, IOCP, etc
|
||||
bool buffered_io = false;
|
||||
|
||||
if (buffered_io) {
|
||||
std::cin >> *con;
|
||||
} else {
|
||||
char a;
|
||||
while(std::cin.get(a)) {
|
||||
con->readsome(&a,1);
|
||||
}
|
||||
}
|
||||
} catch (const std::exception & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (websocketpp::lib::error_code e) {
|
||||
std::cout << e.message() << std::endl;
|
||||
} catch (...) {
|
||||
std::cout << "other exception" << std::endl;
|
||||
}
|
||||
log.close();
|
||||
}
|
||||
23
examples/print_server/SConscript
Normal file
23
examples/print_server/SConscript
Normal file
@@ -0,0 +1,23 @@
|
||||
## Print server example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is avaliable build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env_cpp11.Program('print_server', ["print_server.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','regex'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('print_server', ["print_server.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
22
examples/print_server/print_server.cpp
Normal file
22
examples/print_server/print_server.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
|
||||
void on_message(websocketpp::connection_hdl hdl, server::message_ptr msg) {
|
||||
std::cout << msg->get_payload() << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
server print_server;
|
||||
|
||||
print_server.set_message_handler(&on_message);
|
||||
|
||||
print_server.init_asio();
|
||||
print_server.listen(9002);
|
||||
print_server.start_accept();
|
||||
|
||||
print_server.run();
|
||||
}
|
||||
23
examples/subprotocol_server/SConscript
Normal file
23
examples/subprotocol_server/SConscript
Normal file
@@ -0,0 +1,23 @@
|
||||
## Main development example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is avaliable build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env_cpp11.Program('subprotocol_server', ["subprotocol_server.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','regex'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('subprotocol_server', ["subprotocol_server.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
52
examples/subprotocol_server/subprotocol_server.cpp
Normal file
52
examples/subprotocol_server/subprotocol_server.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
|
||||
using websocketpp::connection_hdl;
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
using websocketpp::lib::ref;
|
||||
|
||||
|
||||
bool validate(server & s, connection_hdl hdl) {
|
||||
server::connection_ptr con = s.get_con_from_hdl(hdl);
|
||||
|
||||
std::cout << "Cache-Control: " << con->get_request_header("Cache-Control") << std::endl;
|
||||
|
||||
const std::vector<std::string> & subp_requests = con->get_requested_subprotocols();
|
||||
std::vector<std::string>::const_iterator it;
|
||||
|
||||
for (it = subp_requests.begin(); it != subp_requests.end(); ++it) {
|
||||
std::cout << "Requested: " << *it << std::endl;
|
||||
}
|
||||
|
||||
if (subp_requests.size() > 0) {
|
||||
con->select_subprotocol(subp_requests[0]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
server s;
|
||||
|
||||
s.set_validate_handler(bind(&validate,ref(s),::_1));
|
||||
|
||||
s.init_asio();
|
||||
s.listen(9005);
|
||||
s.start_accept();
|
||||
|
||||
s.run();
|
||||
} catch (const std::exception & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (websocketpp::lib::error_code e) {
|
||||
std::cout << e.message() << std::endl;
|
||||
} catch (...) {
|
||||
std::cout << "other exception" << std::endl;
|
||||
}
|
||||
}
|
||||
23
examples/telemetry_client/SConscript
Normal file
23
examples/telemetry_client/SConscript
Normal file
@@ -0,0 +1,23 @@
|
||||
## Telemetry client example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is avaliable build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env_cpp11.Program('telemetry_client', ["telemetry_client.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','regex','random'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('telemetry_client', ["telemetry_client.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
150
examples/telemetry_client/telemetry_client.cpp
Normal file
150
examples/telemetry_client/telemetry_client.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
// This header pulls in the WebSocket++ abstracted thread support that will
|
||||
// select between boost::thread and std::thread based on how the build system
|
||||
// is configured.
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
|
||||
/**
|
||||
* The telemetry client connects to a WebSocket server and sends a message every
|
||||
* second containing an integer count. This example can be used as the basis for
|
||||
* programs where a client connects and pushes data for logging, stress/load
|
||||
* testing, etc.
|
||||
*/
|
||||
class telemetry_client {
|
||||
public:
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
typedef websocketpp::lib::lock_guard<websocketpp::lib::mutex> scoped_lock;
|
||||
|
||||
telemetry_client() : m_open(false),m_done(false) {
|
||||
// set up access channels to only log interesting things
|
||||
m_client.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_client.set_access_channels(websocketpp::log::alevel::connect);
|
||||
m_client.set_access_channels(websocketpp::log::alevel::disconnect);
|
||||
m_client.set_access_channels(websocketpp::log::alevel::app);
|
||||
|
||||
// Initialize the Asio transport policy
|
||||
m_client.init_asio();
|
||||
|
||||
// Bind the handlers we are using
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::bind;
|
||||
m_client.set_open_handler(bind(&telemetry_client::on_open,this,::_1));
|
||||
m_client.set_close_handler(bind(&telemetry_client::on_close,this,::_1));
|
||||
m_client.set_fail_handler(bind(&telemetry_client::on_fail,this,::_1));
|
||||
}
|
||||
|
||||
// This method will block until the connection is complete
|
||||
void run(const std::string & uri) {
|
||||
// Create a new connection to the given URI
|
||||
websocketpp::lib::error_code ec;
|
||||
client::connection_ptr con = m_client.get_connection(uri, ec);
|
||||
if (ec) {
|
||||
m_client.get_alog().write(websocketpp::log::alevel::app,
|
||||
"Get Connection Error: "+ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab a handle for this connection so we can talk to it in a thread
|
||||
// safe manor after the event loop starts.
|
||||
m_hdl = con->get_handle();
|
||||
|
||||
// Queue the connection. No DNS queries or network connections will be
|
||||
// made until the io_service event loop is run.
|
||||
m_client.connect(con);
|
||||
|
||||
// Create a thread to run the ASIO io_service event loop
|
||||
websocketpp::lib::thread asio_thread(&client::run, &m_client);
|
||||
|
||||
// Create a thread to run the telemetry loop
|
||||
websocketpp::lib::thread telemetry_thread(&telemetry_client::telemetry_loop,this);
|
||||
|
||||
asio_thread.join();
|
||||
telemetry_thread.join();
|
||||
}
|
||||
|
||||
// The open handler will signal that we are ready to start sending telemetry
|
||||
void on_open(websocketpp::connection_hdl hdl) {
|
||||
m_client.get_alog().write(websocketpp::log::alevel::app,
|
||||
"Connection opened, starting telemetry!");
|
||||
|
||||
scoped_lock guard(m_lock);
|
||||
m_open = true;
|
||||
}
|
||||
|
||||
// The close handler will signal that we should stop sending telemetry
|
||||
void on_close(websocketpp::connection_hdl hdl) {
|
||||
m_client.get_alog().write(websocketpp::log::alevel::app,
|
||||
"Connection closed, stopping telemetry!");
|
||||
|
||||
scoped_lock guard(m_lock);
|
||||
m_done = true;
|
||||
}
|
||||
|
||||
// The fail handler will signal that we should stop sending telemetry
|
||||
void on_fail(websocketpp::connection_hdl hdl) {
|
||||
m_client.get_alog().write(websocketpp::log::alevel::app,
|
||||
"Connection failed, stopping telemetry!");
|
||||
|
||||
scoped_lock guard(m_lock);
|
||||
m_done = true;
|
||||
}
|
||||
|
||||
void telemetry_loop() {
|
||||
uint64_t count = 0;
|
||||
std::stringstream val;
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
while(1) {
|
||||
{
|
||||
scoped_lock guard(m_lock);
|
||||
// If the connection has been closed, stop generating telemetry
|
||||
if (m_done) {break;}
|
||||
|
||||
// If the connection hasn't been opened yet wait a bit and retry
|
||||
if (!m_open) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
val.str("");
|
||||
val << "count is " << count++;
|
||||
|
||||
m_client.get_alog().write(websocketpp::log::alevel::app, val.str());
|
||||
m_client.send(m_hdl,val.str(),websocketpp::frame::opcode::text,ec);
|
||||
|
||||
// The most likely error that we will get is that the connection is
|
||||
// not in the right state. Usually this means we tried to send a
|
||||
// message to a connection that was closed or in the process of
|
||||
// closing. While many errors here can be easily recovered from,
|
||||
// in this simple example, we'll stop the telemetry loop.
|
||||
if (ec) {
|
||||
m_client.get_alog().write(websocketpp::log::alevel::app,
|
||||
"Send Error: "+ec.message());
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
private:
|
||||
client m_client;
|
||||
websocketpp::connection_hdl m_hdl;
|
||||
websocketpp::lib::mutex m_lock;
|
||||
bool m_open;
|
||||
bool m_done;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
telemetry_client c;
|
||||
|
||||
std::string uri = "ws://localhost:9002";
|
||||
|
||||
if (argc == 2) {
|
||||
uri = argv[1];
|
||||
}
|
||||
|
||||
c.run(uri);
|
||||
}
|
||||
23
examples/testee_server/SConscript
Normal file
23
examples/testee_server/SConscript
Normal file
@@ -0,0 +1,23 @@
|
||||
## Autobahn Testee Server
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is avaliable build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env_cpp11.Program('testee_server', ["testee_server.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','regex'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('testee_server', ["testee_server.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
76
examples/testee_server/testee_server.cpp
Normal file
76
examples/testee_server/testee_server.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
#include <iostream>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
// pull out the type of messages sent by our config
|
||||
typedef server::message_ptr message_ptr;
|
||||
|
||||
// Define a callback to handle incoming messages
|
||||
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Create a server endpoint
|
||||
server testee_server;
|
||||
|
||||
try {
|
||||
// Total silence
|
||||
testee_server.clear_access_channels(websocketpp::log::alevel::all);
|
||||
testee_server.clear_error_channels(websocketpp::log::alevel::all);
|
||||
|
||||
// Initialize ASIO
|
||||
testee_server.init_asio();
|
||||
|
||||
// Register our message handler
|
||||
testee_server.set_message_handler(bind(&on_message,&testee_server,::_1,::_2));
|
||||
|
||||
// Listen on port 9002
|
||||
testee_server.listen(9002);
|
||||
|
||||
// Start the server accept loop
|
||||
testee_server.start_accept();
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
testee_server.run();
|
||||
} catch (const std::exception & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (websocketpp::lib::error_code e) {
|
||||
std::cout << e.message() << std::endl;
|
||||
} catch (...) {
|
||||
std::cout << "other exception" << std::endl;
|
||||
}
|
||||
}
|
||||
72
init.txt
72
init.txt
@@ -105,3 +105,75 @@ start_accept
|
||||
##################
|
||||
send message
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
########### Other development notes previously from websocketpp.hpp ############
|
||||
/*
|
||||
|
||||
Endpoint
|
||||
- container for connections
|
||||
- Store and forward default connection settings
|
||||
|
||||
Connection
|
||||
- Represents the state and functionality of a single WebSocket session starting
|
||||
with the opening handshake and completing with the closing one.
|
||||
- After a connection is created settings may be applied that will be used for
|
||||
this connection.
|
||||
- Once setup is complete a start method is run and the connection enters its
|
||||
event loop. The connection requests bytes from its transport, then runs those
|
||||
bytes through the appropriate websocket frame processor, and calls handler
|
||||
methods appropriate for the types of frames recieved.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Policies:
|
||||
|
||||
Concurrency
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Concurrency Models
|
||||
Single Thread Async (lock free)
|
||||
- WS++ runs lock free (Access to endpoint and connection from non-WS++ threads is unsafe)
|
||||
- All handlers and networking operations run in a single thread.
|
||||
- Handlers can block each other and network operations
|
||||
- Good for low traffic workflows where connections are independent and requests are short.
|
||||
|
||||
Single Thread Async
|
||||
- Same as lock free version except access to endpoint and connection from non-WS++ threads is safe
|
||||
- Good for workflows where any long running handler job is deferred to a non-WS++ thread for processing.
|
||||
|
||||
Thread Pool (lock free)
|
||||
- WS++ runs lock free (access to endpoint and connection from non-WS++ threads is unsafe)
|
||||
- Handlers and networking operations invoked by multiple threads. Individual connections are serialized.
|
||||
- n handlers will block network operations (n=num_threads)
|
||||
- Allows much better multi-core utilization, does not require end user syncronization as long as all work is performed inside handlers and handlers only reference their own connection. Handler local data must be syncronized.
|
||||
|
||||
Thread pool
|
||||
- Same as lock free version except access to endpoint and connection from non-WS++ threads is safe.
|
||||
|
||||
Thread per connection
|
||||
-
|
||||
|
||||
io_service policies
|
||||
- external vs internal
|
||||
- per endpoint or per connection
|
||||
|
||||
|
||||
message policies?
|
||||
- Control Messages:
|
||||
-- Each connection should have a single control message permanently allocated
|
||||
- Data Messages
|
||||
-- Dynamically allocate a new data message as needed.
|
||||
-- Re-usable pool of data messages per endpoint
|
||||
-- Re-usable pool of data messages per connection
|
||||
|
||||
|
||||
*/
|
||||
|
||||
16
readme.txt
16
readme.txt
@@ -36,6 +36,12 @@ Implimented, needs more testing
|
||||
- exception/error handling
|
||||
- Logging
|
||||
- Client role
|
||||
- Subprotocol negotiation
|
||||
- Hybi 00/Hixie 76 legacy protocol support
|
||||
- Performance tuning
|
||||
- Outgoing Proxy Support
|
||||
- PowerPC support
|
||||
- Visual Studio / Windows support
|
||||
|
||||
Implimented, API not finalized
|
||||
- open_handler
|
||||
@@ -44,11 +50,9 @@ Implimented, API not finalized
|
||||
- http_handler
|
||||
|
||||
Needs work:
|
||||
- PowerPC support
|
||||
- Subprotocol negotiation
|
||||
- Timeouts
|
||||
|
||||
Non-release blocking feature roadmap
|
||||
- Extension support
|
||||
- permessage_compress extension
|
||||
- Visual Studio / Windows support
|
||||
- Hybi 00/Hixie 76 legacy protocol support
|
||||
- Message buffer pool
|
||||
- Performance tuning
|
||||
- Message buffer pool
|
||||
@@ -81,40 +81,49 @@ struct stub_config : public websocketpp::config::core {
|
||||
typedef connection_extension connection_base;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE( connection_extensions ) {
|
||||
stub_config::alog_type alog;
|
||||
struct connection_setup {
|
||||
connection_setup(bool server)
|
||||
: c(server,"",alog,elog,rng) {}
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
stub_config::alog_type alog;
|
||||
stub_config::elog_type elog;
|
||||
stub_config::rng_type rng;
|
||||
websocketpp::connection<stub_config> s(true,"",alog,elog,rng);
|
||||
stub_config::rng_type rng;
|
||||
websocketpp::connection<stub_config> c;
|
||||
};
|
||||
|
||||
/*void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}*/
|
||||
|
||||
void validate_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( connection_extensions ) {
|
||||
connection_setup env(true);
|
||||
|
||||
BOOST_CHECK( s.extension_value == 5 );
|
||||
BOOST_CHECK( s.extension_method() == 5 );
|
||||
BOOST_CHECK( env.c.extension_value == 5 );
|
||||
BOOST_CHECK( env.c.extension_method() == 5 );
|
||||
|
||||
BOOST_CHECK( s.is_server() == true );
|
||||
BOOST_CHECK( env.c.is_server() == true );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( basic_websocket_request ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: ";
|
||||
output+=websocketpp::user_agent;
|
||||
output+="\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
server s;
|
||||
s.set_message_handler(bind(&echo_func,&s,::_1,::_2));
|
||||
|
||||
BOOST_CHECK(run_server_test(s,input) == output);
|
||||
}
|
||||
|
||||
/*
|
||||
BOOST_AUTO_TEST_CASE( basic_websocket_request ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: "+websocketpp::USER_AGENT+"\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
BOOST_CHECK(run_server_test(input) == output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( invalid_websocket_version ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: a\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
std::string output = "HTTP/1.1 400 Bad Request\r\nServer: "+websocketpp::USER_AGENT+"\r\n\r\n";
|
||||
|
||||
BOOST_CHECK(run_server_test(input) == output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( unimplimented_websocket_version ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 14\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 400 Bad Request\r\nSec-WebSocket-Version: 0,7,8,13\r\nServer: "+websocketpp::USER_AGENT+"\r\n\r\n";
|
||||
|
||||
BOOST_CHECK(run_server_test(input) == output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( user_reject_origin ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example2.com\r\n\r\n";
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "connection_tu2.hpp"
|
||||
|
||||
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ std::string run_server_test(std::string input) {
|
||||
server test_server;
|
||||
server::connection_ptr con;
|
||||
|
||||
test_server.set_message_handler(bind(&on_message,&test_server,::_1,::_2));
|
||||
test_server.set_message_handler(bind(&echo_func,&test_server,::_1,::_2));
|
||||
|
||||
std::stringstream output;
|
||||
|
||||
@@ -50,5 +50,22 @@ std::string run_server_test(std::string input) {
|
||||
channel << input;
|
||||
channel >> *con;
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string run_server_test(server& s, std::string input) {
|
||||
server::connection_ptr con;
|
||||
std::stringstream output;
|
||||
|
||||
s.register_ostream(&output);
|
||||
|
||||
con = s.get_connection();
|
||||
con->start();
|
||||
|
||||
std::stringstream channel;
|
||||
|
||||
channel << input;
|
||||
channel >> *con;
|
||||
|
||||
return output.str();
|
||||
}
|
||||
@@ -51,5 +51,6 @@ using websocketpp::lib::bind;
|
||||
}
|
||||
};*/
|
||||
|
||||
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg);
|
||||
std::string run_server_test(std::string input);
|
||||
void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg);
|
||||
std::string run_server_test(std::string input);
|
||||
std::string run_server_test(server & s,std::string input);
|
||||
@@ -236,123 +236,123 @@ BOOST_AUTO_TEST_CASE( extract_parameters ) {
|
||||
p.clear();
|
||||
it = extract_parameters(s2.begin(),s2.end(),p);
|
||||
BOOST_CHECK( it == s2.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
BOOST_CHECK( p.find("foo")->second.size() == 0 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
BOOST_CHECK_EQUAL( p[0].second.size(), 0 );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s3.begin(),s3.end(),p);
|
||||
BOOST_CHECK( it == s3.begin()+5 );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
BOOST_CHECK( p.find("foo")->second.size() == 0 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
BOOST_CHECK_EQUAL( p[0].second.size(), 0 );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s4.begin(),s4.end(),p);
|
||||
BOOST_CHECK( it == s4.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
BOOST_CHECK( p.find("foo")->second.size() == 0 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
BOOST_CHECK_EQUAL( p[0].second.size(), 0 );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s5.begin(),s5.end(),p);
|
||||
BOOST_CHECK( it == s5.end() );
|
||||
BOOST_CHECK( p.size() == 2 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
BOOST_CHECK( p.find("foo")->second.size() == 0 );
|
||||
BOOST_CHECK( p.find("bar") != p.end() );
|
||||
BOOST_CHECK( p.find("bar")->second.size() == 0 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 2 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
BOOST_CHECK_EQUAL( p[0].second.size(), 0 );
|
||||
BOOST_CHECK( p[1].first == "bar" );
|
||||
BOOST_CHECK_EQUAL( p[1].second.size(), 0 );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s6.begin(),s6.end(),p);
|
||||
BOOST_CHECK( it == s6.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 1 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 1 );
|
||||
BOOST_CHECK( a.find("bar") != a.end() );
|
||||
BOOST_CHECK( a.find("bar")->second == "" );
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "" );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s7.begin(),s7.end(),p);
|
||||
BOOST_CHECK( it == s7.end() );
|
||||
BOOST_CHECK( p.size() == 2 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 1 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 2 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 1 );
|
||||
BOOST_CHECK( a.find("baz") != a.end() );
|
||||
BOOST_CHECK( a.find("baz")->second == "" );
|
||||
BOOST_CHECK( p.find("bar") != p.end() );
|
||||
a = p.find("bar")->second;
|
||||
BOOST_CHECK( a.size() == 0 );
|
||||
BOOST_CHECK_EQUAL( a.find("baz")->second, "" );
|
||||
BOOST_CHECK( p[1].first == "bar" );
|
||||
a = p[1].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 0 );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s8.begin(),s8.end(),p);
|
||||
BOOST_CHECK( it == s8.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 2 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 2 );
|
||||
BOOST_CHECK( a.find("bar") != a.end() );
|
||||
BOOST_CHECK( a.find("bar")->second == "" );
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "" );
|
||||
BOOST_CHECK( a.find("baz") != a.end() );
|
||||
BOOST_CHECK( a.find("baz")->second == "" );
|
||||
BOOST_CHECK_EQUAL( a.find("baz")->second, "" );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s9.begin(),s9.end(),p);
|
||||
BOOST_CHECK( it == s9.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 1 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 1 );
|
||||
BOOST_CHECK( a.find("bar") != a.end() );
|
||||
BOOST_CHECK( a.find("bar")->second == "baz" );
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "baz" );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s10.begin(),s10.end(),p);
|
||||
BOOST_CHECK( it == s10.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 2 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 2 );
|
||||
BOOST_CHECK( a.find("bar") != a.end() );
|
||||
BOOST_CHECK( a.find("bar")->second == "baz" );
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "baz" );
|
||||
BOOST_CHECK( a.find("boo") != a.end() );
|
||||
BOOST_CHECK( a.find("boo")->second == "" );
|
||||
BOOST_CHECK_EQUAL( a.find("boo")->second, "" );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s11.begin(),s11.end(),p);
|
||||
BOOST_CHECK( it == s11.end() );
|
||||
BOOST_CHECK( p.size() == 2 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 2 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 2 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 2 );
|
||||
BOOST_CHECK( a.find("bar") != a.end() );
|
||||
BOOST_CHECK( a.find("bar")->second == "baz" );
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "baz" );
|
||||
BOOST_CHECK( a.find("boo") != a.end() );
|
||||
BOOST_CHECK( a.find("boo")->second == "" );
|
||||
a = p.find("bob")->second;
|
||||
BOOST_CHECK( a.size() == 0 );
|
||||
BOOST_CHECK_EQUAL( a.find("boo")->second, "" );
|
||||
a = p[1].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 0 );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s12.begin(),s12.end(),p);
|
||||
BOOST_CHECK( it == s12.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 1 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 1 );
|
||||
BOOST_CHECK( a.find("bar") != a.end() );
|
||||
BOOST_CHECK( a.find("bar")->second == "a b c" );
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "a b c" );
|
||||
|
||||
p.clear();
|
||||
it = extract_parameters(s13.begin(),s13.end(),p);
|
||||
BOOST_CHECK( it == s13.end() );
|
||||
BOOST_CHECK( p.size() == 1 );
|
||||
BOOST_CHECK( p.find("foo") != p.end() );
|
||||
a = p.find("foo")->second;
|
||||
BOOST_CHECK( a.size() == 1 );
|
||||
BOOST_CHECK_EQUAL( p.size(), 1 );
|
||||
BOOST_CHECK( p[0].first == "foo" );
|
||||
a = p[0].second;
|
||||
BOOST_CHECK_EQUAL( a.size(), 1 );
|
||||
BOOST_CHECK( a.find("bar") != a.end() );
|
||||
BOOST_CHECK( a.find("bar")->second == "a \"b\" c" );
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "a \"b\" c" );
|
||||
}
|
||||
|
||||
|
||||
@@ -829,6 +829,29 @@ BOOST_AUTO_TEST_CASE( plain_http_response ) {
|
||||
BOOST_CHECK( r.get_body() == "<!doctype html>\n<html>\n<head>\n<title>Thor</title>\n</head>\n<body> \n<p>Thor</p>\n</body>" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( parse_istream ) {
|
||||
websocketpp::http::parser::response r;
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
s << "HTTP/1.1 200 OK\r\nDate: Thu, 10 May 2012 11:59:25 GMT\r\nServer: Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.8 with Suhosin-Patch\r\nLast-Modified: Tue, 30 Mar 2010 17:41:28 GMT\r\nETag: \"16799d-55-4830823a78200\"\r\nAccept-Ranges: bytes\r\nContent-Length: 85\r\nVary: Accept-Encoding\r\nContent-Type: text/html\r\n\r\n<!doctype html>\n<html>\n<head>\n<title>Thor</title>\n</head>\n<body> \n<p>Thor</p>\n</body>";
|
||||
|
||||
bool exception = false;
|
||||
size_t pos = 0;
|
||||
|
||||
try {
|
||||
pos += r.consume(s);
|
||||
} catch (std::exception &e) {
|
||||
exception = true;
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL( exception, false );
|
||||
BOOST_CHECK_EQUAL( pos, 405 );
|
||||
BOOST_CHECK_EQUAL( r.headers_ready(), true );
|
||||
BOOST_CHECK_EQUAL( r.ready(), true );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( write_request_basic ) {
|
||||
websocketpp::http::parser::request r;
|
||||
|
||||
@@ -866,6 +889,5 @@ BOOST_AUTO_TEST_CASE( write_request_with_body ) {
|
||||
r.replace_header("Content-Type","application/x-www-form-urlencoded");
|
||||
r.set_body("licenseID=string&content=string¶msXML=string");
|
||||
|
||||
std::cout << r.raw() << std::endl;
|
||||
BOOST_CHECK( r.raw() == raw );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2013, 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:
|
||||
@@ -25,7 +25,7 @@
|
||||
*
|
||||
*/
|
||||
//#define BOOST_TEST_DYN_LINK
|
||||
#define BOOST_TEST_MODULE extension_permessage_compress
|
||||
#define BOOST_TEST_MODULE extension_permessage_deflate
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <iostream>
|
||||
@@ -33,25 +33,25 @@
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
|
||||
#include <websocketpp/http/request.hpp>
|
||||
#include <websocketpp/extensions/permessage_compress/enabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
|
||||
|
||||
struct config {
|
||||
typedef websocketpp::http::parser::request request_type;
|
||||
};
|
||||
typedef websocketpp::extensions::permessage_compress::enabled<config>
|
||||
typedef websocketpp::extensions::permessage_deflate::enabled<config>
|
||||
compressor_type;
|
||||
|
||||
using namespace websocketpp;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( deflate_init ) {
|
||||
compressor_type compressor;
|
||||
/*compressor_type compressor;
|
||||
websocketpp::http::parser::attribute_list attributes;
|
||||
std::pair<lib::error_code,std::string> neg_ret;
|
||||
|
||||
neg_ret = compressor.negotiate(attributes);
|
||||
|
||||
BOOST_CHECK_EQUAL( neg_ret.first,
|
||||
extensions::permessage_compress::error::invalid_parameters );
|
||||
extensions::permessage_deflate::error::invalid_parameters );*/
|
||||
|
||||
/**
|
||||
* Window size is primarily controlled by the writer. A stream can only be
|
||||
|
||||
@@ -47,139 +47,226 @@ struct stub_config {
|
||||
con_msg_manager_type;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
stub_config::request_type r;
|
||||
stub_config::response_type response;
|
||||
stub_config::con_msg_manager_type::ptr msg_manager;
|
||||
websocketpp::processor::hybi00<stub_config> p(false,true,msg_manager);
|
||||
struct processor_setup {
|
||||
processor_setup(bool server)
|
||||
: msg_manager(new stub_config::con_msg_manager_type())
|
||||
, p(false,server,msg_manager) {}
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
stub_config::con_msg_manager_type::ptr msg_manager;
|
||||
stub_config::request_type req;
|
||||
stub_config::response_type res;
|
||||
websocketpp::processor::hybi00<stub_config> p;
|
||||
};
|
||||
|
||||
typedef stub_config::message_type::ptr message_ptr;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
processor_setup env(true);
|
||||
|
||||
std::string handshake = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nOrigin: http://example.com\r\nSec-WebSocket-Key1: 3e6b263 4 17 80\r\nSec-WebSocket-Key2: 17 9 G`ZD9 2 2b 7X 3 /r90\r\n\r\n";
|
||||
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
r.replace_header("Sec-WebSocket-Key3","WjN}|M(6");
|
||||
env.req.consume(handshake.c_str(),handshake.size());
|
||||
env.req.replace_header("Sec-WebSocket-Key3","WjN}|M(6");
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(r));
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == p.get_version());
|
||||
ec = p.validate_handshake(r);
|
||||
BOOST_CHECK(!ec);
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(env.req));
|
||||
BOOST_CHECK_EQUAL(websocketpp::processor::get_websocket_version(env.req), env.p.get_version());
|
||||
env.ec = env.p.validate_handshake(env.req);
|
||||
BOOST_CHECK(!env.ec);
|
||||
|
||||
websocketpp::uri_ptr u;
|
||||
bool exception = false;
|
||||
|
||||
BOOST_CHECK_NO_THROW( u = env.p.get_uri(env.req) );
|
||||
|
||||
try {
|
||||
u = p.get_uri(r);
|
||||
} catch (const websocketpp::uri_exception& e) {
|
||||
exception = true;
|
||||
}
|
||||
BOOST_CHECK_EQUAL(u->get_secure(), false);
|
||||
BOOST_CHECK_EQUAL(u->get_host(), "www.example.com");
|
||||
BOOST_CHECK_EQUAL(u->get_resource(), "/");
|
||||
BOOST_CHECK_EQUAL(u->get_port(), websocketpp::URI_DEFAULT_PORT);
|
||||
|
||||
BOOST_CHECK(exception == false);
|
||||
BOOST_CHECK(u->get_secure() == false);
|
||||
BOOST_CHECK(u->get_host() == "www.example.com");
|
||||
BOOST_CHECK(u->get_resource() == "/");
|
||||
BOOST_CHECK(u->get_port() == websocketpp::URI_DEFAULT_PORT);
|
||||
env.p.process_handshake(env.req,"",env.res);
|
||||
|
||||
p.process_handshake(r,response);
|
||||
|
||||
BOOST_CHECK(response.get_header("Connection") == "Upgrade");
|
||||
BOOST_CHECK(response.get_header("Upgrade") == "websocket");
|
||||
BOOST_CHECK(response.get_header("Sec-WebSocket-Origin") == "http://example.com");
|
||||
BOOST_CHECK_EQUAL(env.res.get_header("Connection"), "Upgrade");
|
||||
BOOST_CHECK_EQUAL(env.res.get_header("Upgrade"), "WebSocket");
|
||||
BOOST_CHECK_EQUAL(env.res.get_header("Sec-WebSocket-Origin"), "http://example.com");
|
||||
|
||||
BOOST_CHECK(response.get_header("Sec-WebSocket-Location") == "ws://www.example.com/");
|
||||
BOOST_CHECK(response.get_header("Sec-WebSocket-Key3") == "n`9eBk9z$R8pOtVb");
|
||||
BOOST_CHECK_EQUAL(env.res.get_header("Sec-WebSocket-Location"), "ws://www.example.com/");
|
||||
BOOST_CHECK_EQUAL(env.res.get_header("Sec-WebSocket-Key3"), "n`9eBk9z$R8pOtVb");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( non_get_method ) {
|
||||
stub_config::request_type r;
|
||||
stub_config::con_msg_manager_type::ptr msg_manager;
|
||||
websocketpp::processor::hybi00<stub_config> p(false,true,msg_manager);
|
||||
websocketpp::lib::error_code ec;
|
||||
processor_setup env(true);
|
||||
|
||||
std::string handshake = "POST / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Key1: 3e6b263 4 17 80\r\nSec-WebSocket-Key2: 17 9 G`ZD9 2 2b 7X 3 /r90\r\n\r\n";
|
||||
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
r.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
env.req.consume(handshake.c_str(),handshake.size());
|
||||
env.req.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(r));
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == p.get_version());
|
||||
ec = p.validate_handshake(r);
|
||||
BOOST_CHECK( ec == websocketpp::processor::error::invalid_http_method );
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(env.req));
|
||||
BOOST_CHECK_EQUAL(websocketpp::processor::get_websocket_version(env.req), env.p.get_version());
|
||||
BOOST_CHECK_EQUAL( env.p.validate_handshake(env.req), websocketpp::processor::error::invalid_http_method );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( old_http_version ) {
|
||||
stub_config::request_type r;
|
||||
stub_config::con_msg_manager_type::ptr msg_manager;
|
||||
websocketpp::processor::hybi00<stub_config> p(false,true,msg_manager);
|
||||
websocketpp::lib::error_code ec;
|
||||
processor_setup env(true);
|
||||
|
||||
std::string handshake = "GET / HTTP/1.0\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Key1: 3e6b263 4 17 80\r\nSec-WebSocket-Key2: 17 9 G`ZD9 2 2b 7X 3 /r90\r\n\r\n";
|
||||
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
r.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
env.req.consume(handshake.c_str(),handshake.size());
|
||||
env.req.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(r));
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == p.get_version());
|
||||
ec = p.validate_handshake(r);
|
||||
BOOST_CHECK( ec == websocketpp::processor::error::invalid_http_version );
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(env.req));
|
||||
BOOST_CHECK_EQUAL(websocketpp::processor::get_websocket_version(env.req), env.p.get_version());
|
||||
BOOST_CHECK_EQUAL( env.p.validate_handshake(env.req), websocketpp::processor::error::invalid_http_version );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( missing_handshake_key1 ) {
|
||||
stub_config::request_type r;
|
||||
stub_config::con_msg_manager_type::ptr msg_manager;
|
||||
websocketpp::processor::hybi00<stub_config> p(false,true,msg_manager);
|
||||
websocketpp::lib::error_code ec;
|
||||
processor_setup env(true);
|
||||
|
||||
std::string handshake = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Key1: 3e6b263 4 17 80\r\n\r\n";
|
||||
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
r.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
env.req.consume(handshake.c_str(),handshake.size());
|
||||
env.req.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(r));
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == p.get_version());
|
||||
ec = p.validate_handshake(r);
|
||||
BOOST_CHECK( ec == websocketpp::processor::error::missing_required_header );
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(env.req));
|
||||
BOOST_CHECK_EQUAL(websocketpp::processor::get_websocket_version(env.req), env.p.get_version());
|
||||
BOOST_CHECK_EQUAL( env.p.validate_handshake(env.req), websocketpp::processor::error::missing_required_header );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( missing_handshake_key2 ) {
|
||||
stub_config::request_type r;
|
||||
stub_config::con_msg_manager_type::ptr msg_manager;
|
||||
websocketpp::processor::hybi00<stub_config> p(false,true,msg_manager);
|
||||
websocketpp::lib::error_code ec;
|
||||
processor_setup env(true);
|
||||
|
||||
std::string handshake = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Key2: 17 9 G`ZD9 2 2b 7X 3 /r90\r\n\r\n";
|
||||
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
r.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
env.req.consume(handshake.c_str(),handshake.size());
|
||||
env.req.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(r));
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == p.get_version());
|
||||
ec = p.validate_handshake(r);
|
||||
BOOST_CHECK( ec == websocketpp::processor::error::missing_required_header );
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(env.req));
|
||||
BOOST_CHECK_EQUAL(websocketpp::processor::get_websocket_version(env.req), env.p.get_version());
|
||||
BOOST_CHECK_EQUAL( env.p.validate_handshake(env.req), websocketpp::processor::error::missing_required_header );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bad_host ) {
|
||||
stub_config::request_type r;
|
||||
stub_config::con_msg_manager_type::ptr msg_manager;
|
||||
websocketpp::processor::hybi00<stub_config> p(false,true,msg_manager);
|
||||
processor_setup env(true);
|
||||
websocketpp::uri_ptr u;
|
||||
bool exception = false;
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
std::string handshake = "GET / HTTP/1.1\r\nHost: www.example.com:70000\r\nConnection: upgrade\r\nUpgrade: websocket\r\nOrigin: http://example.com\r\nSec-WebSocket-Key1: 3e6b263 4 17 80\r\nSec-WebSocket-Key2: 17 9 G`ZD9 2 2b 7X 3 /r90\r\n\r\n";
|
||||
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
r.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
env.req.consume(handshake.c_str(),handshake.size());
|
||||
env.req.replace_header("Sec-WebSocket-Key3","janelle!");
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(r));
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == p.get_version());
|
||||
ec = p.validate_handshake(r);
|
||||
BOOST_CHECK( !ec );
|
||||
BOOST_CHECK(websocketpp::processor::is_websocket_handshake(env.req));
|
||||
BOOST_CHECK_EQUAL(websocketpp::processor::get_websocket_version(env.req), env.p.get_version());
|
||||
BOOST_CHECK( !env.p.validate_handshake(env.req) );
|
||||
|
||||
BOOST_CHECK_THROW( u = env.p.get_uri(env.req), websocketpp::uri_exception );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_subprotocols ) {
|
||||
processor_setup env(true);
|
||||
|
||||
try {
|
||||
u = p.get_uri(r);
|
||||
} catch (const websocketpp::uri_exception& e) {
|
||||
exception = true;
|
||||
std::vector<std::string> subps;
|
||||
|
||||
BOOST_CHECK( !env.p.extract_subprotocols(env.req,subps) );
|
||||
BOOST_CHECK_EQUAL( subps.size(), 0 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( prepare_data_frame_null ) {
|
||||
processor_setup env(true);
|
||||
|
||||
message_ptr in = env.msg_manager->get_message();
|
||||
message_ptr out = env.msg_manager->get_message();
|
||||
message_ptr invalid;
|
||||
|
||||
|
||||
// empty pointers arguements should return sane error
|
||||
BOOST_CHECK_EQUAL( env.p.prepare_data_frame(invalid,invalid), websocketpp::processor::error::invalid_arguments );
|
||||
|
||||
BOOST_CHECK_EQUAL( env.p.prepare_data_frame(in,invalid), websocketpp::processor::error::invalid_arguments );
|
||||
|
||||
BOOST_CHECK_EQUAL( env.p.prepare_data_frame(invalid,out), websocketpp::processor::error::invalid_arguments );
|
||||
|
||||
// test valid opcodes
|
||||
// text (1) should be the only valid opcode
|
||||
for (int i = 0; i < 0xF; i++) {
|
||||
in->set_opcode(websocketpp::frame::opcode::value(i));
|
||||
|
||||
env.ec = env.p.prepare_data_frame(in,out);
|
||||
|
||||
if (i != 1) {
|
||||
BOOST_CHECK_EQUAL( env.ec, websocketpp::processor::error::invalid_opcode );
|
||||
} else {
|
||||
BOOST_CHECK_NE( env.ec, websocketpp::processor::error::invalid_opcode );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_CHECK(exception == true);
|
||||
/*
|
||||
* TODO: tests for invalid UTF8
|
||||
char buf[2] = {0x00, 0x00};
|
||||
|
||||
in->set_opcode(websocketpp::frame::opcode::text);
|
||||
in->set_payload("foo");
|
||||
|
||||
env.ec = env.p.prepare_data_frame(in,out);
|
||||
BOOST_CHECK_EQUAL( env.ec, websocketpp::processor::error::invalid_payload );
|
||||
*/
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( prepare_data_frame ) {
|
||||
processor_setup env(true);
|
||||
|
||||
message_ptr in = env.msg_manager->get_message();
|
||||
message_ptr out = env.msg_manager->get_message();
|
||||
|
||||
in->set_opcode(websocketpp::frame::opcode::text);
|
||||
in->set_payload("foo");
|
||||
|
||||
env.ec = env.p.prepare_data_frame(in,out);
|
||||
|
||||
unsigned char raw_header[1] = {0x00};
|
||||
unsigned char raw_payload[4] = {0x66,0x6f,0x6f,0xff};
|
||||
|
||||
BOOST_CHECK( !env.ec );
|
||||
BOOST_CHECK_EQUAL( out->get_header(), std::string(reinterpret_cast<char*>(raw_header),1) );
|
||||
BOOST_CHECK_EQUAL( out->get_payload(), std::string(reinterpret_cast<char*>(raw_payload),4) );
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( empty_consume ) {
|
||||
uint8_t frame[2] = {0x00,0x00};
|
||||
|
||||
processor_setup env(true);
|
||||
|
||||
size_t ret = env.p.consume(frame,0,env.ec);
|
||||
|
||||
BOOST_CHECK_EQUAL( ret, 0);
|
||||
BOOST_CHECK( !env.ec );
|
||||
BOOST_CHECK_EQUAL( env.p.ready(), false );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( empty_frame ) {
|
||||
uint8_t frame[2] = {0x00, 0xff};
|
||||
|
||||
processor_setup env(true);
|
||||
|
||||
size_t ret = env.p.consume(frame,2,env.ec);
|
||||
|
||||
BOOST_CHECK_EQUAL( ret, 2);
|
||||
BOOST_CHECK( !env.ec );
|
||||
BOOST_CHECK_EQUAL( env.p.ready(), true );
|
||||
BOOST_CHECK_EQUAL( env.p.get_message()->get_payload(), "" );
|
||||
BOOST_CHECK_EQUAL( env.p.ready(), false );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( short_frame ) {
|
||||
uint8_t frame[5] = {0x00, 0x66, 0x6f, 0x6f, 0xff};
|
||||
|
||||
processor_setup env(true);
|
||||
|
||||
size_t ret = env.p.consume(frame,5,env.ec);
|
||||
|
||||
BOOST_CHECK_EQUAL( ret, 5);
|
||||
BOOST_CHECK( !env.ec );
|
||||
BOOST_CHECK_EQUAL( env.p.ready(), true );
|
||||
BOOST_CHECK_EQUAL( env.p.get_message()->get_payload(), "foo" );
|
||||
BOOST_CHECK_EQUAL( env.p.ready(), false );
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include <websocketpp/http/response.hpp>
|
||||
#include <websocketpp/message_buffer/message.hpp>
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
#include <websocketpp/extensions/permessage_compress/disabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
#include <websocketpp/random/none.hpp>
|
||||
|
||||
struct stub_config {
|
||||
@@ -56,12 +56,12 @@ struct stub_config {
|
||||
/// Extension specific config
|
||||
|
||||
/// permessage_compress_config
|
||||
struct permessage_compress_config {
|
||||
struct permessage_deflate_config {
|
||||
typedef stub_config::request_type request_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::extensions::permessage_compress::disabled
|
||||
<permessage_compress_config> permessage_compress_type;
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
@@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
BOOST_CHECK(u->get_resource() == "/");
|
||||
BOOST_CHECK(u->get_port() == websocketpp::uri_default_port);
|
||||
|
||||
p.process_handshake(r,response);
|
||||
p.process_handshake(r,"",response);
|
||||
|
||||
BOOST_CHECK(response.get_header("Connection") == "upgrade");
|
||||
BOOST_CHECK(response.get_header("Upgrade") == "websocket");
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include <websocketpp/http/response.hpp>
|
||||
#include <websocketpp/message_buffer/message.hpp>
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
#include <websocketpp/extensions/permessage_compress/disabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
#include <websocketpp/random/none.hpp>
|
||||
|
||||
struct stub_config {
|
||||
@@ -55,13 +55,13 @@ struct stub_config {
|
||||
|
||||
/// Extension specific config
|
||||
|
||||
/// permessage_compress_config
|
||||
struct permessage_compress_config {
|
||||
/// permessage_deflate_config
|
||||
struct permessage_deflate_config {
|
||||
typedef stub_config::request_type request_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::extensions::permessage_compress::disabled
|
||||
<permessage_compress_config> permessage_compress_type;
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
@@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
BOOST_CHECK(u->get_resource() == "/");
|
||||
BOOST_CHECK(u->get_port() == websocketpp::uri_default_port);
|
||||
|
||||
p.process_handshake(r,response);
|
||||
p.process_handshake(r,"",response);
|
||||
|
||||
BOOST_CHECK(response.get_header("Connection") == "upgrade");
|
||||
BOOST_CHECK(response.get_header("Upgrade") == "websocket");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2013, 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:
|
||||
@@ -39,8 +39,8 @@
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
#include <websocketpp/random/none.hpp>
|
||||
|
||||
#include <websocketpp/extensions/permessage_compress/disabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_compress/enabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
|
||||
|
||||
struct stub_config {
|
||||
typedef websocketpp::http::parser::request request_type;
|
||||
@@ -53,12 +53,12 @@ struct stub_config {
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
struct permessage_compress_config {
|
||||
struct permessage_deflate_config {
|
||||
typedef stub_config::request_type request_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::extensions::permessage_compress::disabled
|
||||
<permessage_compress_config> permessage_compress_type;
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
static const bool enable_extensions = false;
|
||||
};
|
||||
@@ -74,14 +74,14 @@ struct stub_config_ext {
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
struct permessage_compress_config {
|
||||
struct permessage_deflate_config {
|
||||
typedef stub_config_ext::request_type request_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::extensions::permessage_compress::enabled
|
||||
<permessage_compress_config> permessage_compress_type;
|
||||
typedef websocketpp::extensions::permessage_deflate::enabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
static const bool enable_extensions = false;
|
||||
static const bool enable_extensions = true;
|
||||
};
|
||||
|
||||
typedef stub_config::con_msg_manager_type con_msg_manager_type;
|
||||
@@ -102,6 +102,19 @@ struct processor_setup {
|
||||
websocketpp::processor::hybi13<stub_config> p;
|
||||
};
|
||||
|
||||
struct processor_setup_ext {
|
||||
processor_setup_ext(bool server)
|
||||
: msg_manager(new con_msg_manager_type())
|
||||
, p(false,server,msg_manager,rng) {}
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
con_msg_manager_type::ptr msg_manager;
|
||||
stub_config::rng_type rng;
|
||||
stub_config::request_type req;
|
||||
stub_config::response_type res;
|
||||
websocketpp::processor::hybi13<stub_config_ext> p;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
processor_setup env(true);
|
||||
|
||||
@@ -122,7 +135,7 @@ BOOST_AUTO_TEST_CASE( exact_match ) {
|
||||
BOOST_CHECK_EQUAL(u->get_resource(), "/");
|
||||
BOOST_CHECK_EQUAL(u->get_port(), websocketpp::uri_default_port);
|
||||
|
||||
env.p.process_handshake(env.req,env.res);
|
||||
env.p.process_handshake(env.req,"",env.res);
|
||||
|
||||
BOOST_CHECK_EQUAL(env.res.get_header("Connection"), "upgrade");
|
||||
BOOST_CHECK_EQUAL(env.res.get_header("Upgrade"), "websocket");
|
||||
@@ -482,7 +495,7 @@ BOOST_AUTO_TEST_CASE( client_handshake_request ) {
|
||||
|
||||
websocketpp::uri_ptr u(new websocketpp::uri("ws://localhost/"));
|
||||
|
||||
env.p.client_handshake_request(env.req,u);
|
||||
env.p.client_handshake_request(env.req,u, std::vector<std::string>());
|
||||
|
||||
BOOST_CHECK_EQUAL( env.req.get_method(), "GET" );
|
||||
BOOST_CHECK_EQUAL( env.req.get_version(), "HTTP/1.1");
|
||||
@@ -554,4 +567,96 @@ BOOST_AUTO_TEST_CASE( client_handshake_response ) {
|
||||
env.res.consume(res.data(),res.size());
|
||||
|
||||
BOOST_CHECK( !env.p.validate_server_handshake_response(env.req,env.res) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extensions_disabled ) {
|
||||
processor_setup env(true);
|
||||
|
||||
env.req.replace_header("Sec-WebSocket-Extensions","");
|
||||
|
||||
std::pair<websocketpp::lib::error_code,std::string> neg_results;
|
||||
neg_results = env.p.negotiate_extensions(env.req);
|
||||
|
||||
BOOST_CHECK_EQUAL( neg_results.first, websocketpp::processor::error::extensions_disabled );
|
||||
BOOST_CHECK_EQUAL( neg_results.second, "" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extension_negotiation_blank ) {
|
||||
processor_setup_ext env(true);
|
||||
|
||||
env.req.replace_header("Sec-WebSocket-Extensions","");
|
||||
|
||||
std::pair<websocketpp::lib::error_code,std::string> neg_results;
|
||||
neg_results = env.p.negotiate_extensions(env.req);
|
||||
|
||||
BOOST_CHECK( !neg_results.first );
|
||||
BOOST_CHECK_EQUAL( neg_results.second, "" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extension_negotiation_unknown ) {
|
||||
processor_setup_ext env(true);
|
||||
|
||||
env.req.replace_header("Sec-WebSocket-Extensions","foo");
|
||||
|
||||
std::pair<websocketpp::lib::error_code,std::string> neg_results;
|
||||
neg_results = env.p.negotiate_extensions(env.req);
|
||||
|
||||
BOOST_CHECK( !neg_results.first );
|
||||
BOOST_CHECK_EQUAL( neg_results.second, "" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_subprotocols_empty ) {
|
||||
processor_setup env(true);
|
||||
std::vector<std::string> subps;
|
||||
|
||||
BOOST_CHECK( !env.p.extract_subprotocols(env.req,subps) );
|
||||
BOOST_CHECK_EQUAL( subps.size(), 0 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_subprotocols_one ) {
|
||||
processor_setup env(true);
|
||||
std::vector<std::string> subps;
|
||||
|
||||
env.req.replace_header("Sec-WebSocket-Protocol","foo");
|
||||
|
||||
BOOST_CHECK( !env.p.extract_subprotocols(env.req,subps) );
|
||||
BOOST_REQUIRE_EQUAL( subps.size(), 1 );
|
||||
BOOST_CHECK_EQUAL( subps[0], "foo" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_subprotocols_multiple ) {
|
||||
processor_setup env(true);
|
||||
std::vector<std::string> subps;
|
||||
|
||||
env.req.replace_header("Sec-WebSocket-Protocol","foo,bar");
|
||||
|
||||
BOOST_CHECK( !env.p.extract_subprotocols(env.req,subps) );
|
||||
BOOST_REQUIRE_EQUAL( subps.size(), 2 );
|
||||
BOOST_CHECK_EQUAL( subps[0], "foo" );
|
||||
BOOST_CHECK_EQUAL( subps[1], "bar" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_subprotocols_invalid) {
|
||||
processor_setup env(true);
|
||||
std::vector<std::string> subps;
|
||||
|
||||
env.req.replace_header("Sec-WebSocket-Protocol","foo,bar,,,,");
|
||||
|
||||
BOOST_CHECK_EQUAL( env.p.extract_subprotocols(env.req,subps), websocketpp::processor::error::make_error_code(websocketpp::processor::error::subprotocol_parse_error) );
|
||||
BOOST_CHECK_EQUAL( subps.size(), 0 );
|
||||
}
|
||||
|
||||
/*BOOST_AUTO_TEST_CASE( extension_negotiation_permessage_deflate ) {
|
||||
processor_setup_ext env(true);
|
||||
|
||||
env.req.replace_header("Sec-WebSocket-Extensions","permessage-deflate; foo; bar=\"x x\"");
|
||||
|
||||
std::pair<websocketpp::lib::error_code,std::string> neg_results;
|
||||
neg_results = env.p.negotiate_extensions(env.req);
|
||||
|
||||
std::cout << neg_results.first.message() << neg_results.second << std::endl;
|
||||
|
||||
BOOST_CHECK( !neg_results.first );
|
||||
BOOST_CHECK_EQUAL( neg_results.second, "permessage-deflate" );
|
||||
}*/
|
||||
|
||||
|
||||
@@ -133,7 +133,63 @@ BOOST_AUTO_TEST_CASE( connect_con ) {
|
||||
channel2 >> *con;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( select_subprotocol ) {
|
||||
client c;
|
||||
websocketpp::lib::error_code ec;
|
||||
using websocketpp::error::make_error_code;
|
||||
|
||||
connection_ptr con = c.get_connection("ws://localhost/", ec);
|
||||
|
||||
BOOST_CHECK( con );
|
||||
|
||||
con->select_subprotocol("foo",ec);
|
||||
BOOST_CHECK_EQUAL( ec , make_error_code(websocketpp::error::server_only) );
|
||||
BOOST_CHECK_THROW( con->select_subprotocol("foo") , websocketpp::lib::error_code );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( add_subprotocols_invalid ) {
|
||||
client c;
|
||||
websocketpp::lib::error_code ec;
|
||||
using websocketpp::error::make_error_code;
|
||||
|
||||
connection_ptr con = c.get_connection("ws://localhost/", ec);
|
||||
BOOST_CHECK( con );
|
||||
|
||||
con->add_subprotocol("",ec);
|
||||
BOOST_CHECK_EQUAL( ec , make_error_code(websocketpp::error::invalid_subprotocol) );
|
||||
BOOST_CHECK_THROW( con->add_subprotocol("") , websocketpp::lib::error_code );
|
||||
|
||||
con->add_subprotocol("foo,bar",ec);
|
||||
BOOST_CHECK_EQUAL( ec , make_error_code(websocketpp::error::invalid_subprotocol) );
|
||||
BOOST_CHECK_THROW( con->add_subprotocol("foo,bar") , websocketpp::lib::error_code );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( add_subprotocols ) {
|
||||
client c;
|
||||
websocketpp::lib::error_code ec;
|
||||
std::stringstream out;
|
||||
std::string o;
|
||||
|
||||
c.register_ostream(&out);
|
||||
|
||||
connection_ptr con = c.get_connection("ws://localhost/", ec);
|
||||
BOOST_CHECK( con );
|
||||
|
||||
con->add_subprotocol("foo",ec);
|
||||
BOOST_CHECK( !ec );
|
||||
|
||||
BOOST_CHECK_NO_THROW( con->add_subprotocol("bar") );
|
||||
|
||||
c.connect(con);
|
||||
|
||||
o = out.str();
|
||||
websocketpp::http::parser::request r;
|
||||
r.consume(o.data(),o.size());
|
||||
|
||||
std::cout << o << std::endl;
|
||||
|
||||
BOOST_CHECK( r.ready() );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Protocol"), "foo, bar");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,10 +30,19 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// Test Environment:
|
||||
// server, no TLS, no locks, iostream based transport
|
||||
#include <websocketpp/config/core.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
struct stub_config : public websocketpp::config::core {
|
||||
typedef websocketpp::server<websocketpp::config::core> server;
|
||||
typedef websocketpp::config::core::message_type::ptr message_ptr;
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
/*struct stub_config : public websocketpp::config::core {
|
||||
typedef core::concurrency_type concurrency_type;
|
||||
|
||||
typedef core::request_type request_type;
|
||||
@@ -51,9 +60,186 @@ struct stub_config : public websocketpp::config::core {
|
||||
typedef core::transport_type transport_type;
|
||||
|
||||
typedef core::endpoint_base endpoint_base;
|
||||
};
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( foo ) {
|
||||
};*/
|
||||
|
||||
/* Run server and return output test rig */
|
||||
std::string run_server_test(server& s, std::string input) {
|
||||
server::connection_ptr con;
|
||||
std::stringstream output;
|
||||
|
||||
s.register_ostream(&output);
|
||||
|
||||
con = s.get_connection();
|
||||
con->start();
|
||||
|
||||
std::stringstream channel;
|
||||
|
||||
channel << input;
|
||||
channel >> *con;
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
/* handler library*/
|
||||
void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}
|
||||
|
||||
bool validate_func_subprotocol(server* s, std::string* out, std::string accept,
|
||||
websocketpp::connection_hdl hdl)
|
||||
{
|
||||
server::connection_ptr con = s->get_con_from_hdl(hdl);
|
||||
|
||||
std::stringstream o;
|
||||
|
||||
const std::vector<std::string> & protocols = con->get_requested_subprotocols();
|
||||
std::vector<std::string>::const_iterator it;
|
||||
|
||||
for (it = protocols.begin(); it != protocols.end(); ++it) {
|
||||
o << *it << ",";
|
||||
}
|
||||
|
||||
*out = o.str();
|
||||
|
||||
if (accept != "") {
|
||||
con->select_subprotocol(accept);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void open_func_subprotocol(server* s, std::string* out, websocketpp::connection_hdl hdl) {
|
||||
server::connection_ptr con = s->get_con_from_hdl(hdl);
|
||||
|
||||
*out = con->get_subprotocol();
|
||||
}
|
||||
|
||||
/* Tests */
|
||||
BOOST_AUTO_TEST_CASE( basic_websocket_request ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: test\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( invalid_websocket_version ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: a\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
std::string output = "HTTP/1.1 400 Bad Request\r\nServer: test\r\n\r\n";
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
//s.set_message_handler(bind(&echo_func,&s,::_1,::_2));
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( unimplimented_websocket_version ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 14\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 400 Bad Request\r\nSec-WebSocket-Version: 0,7,8,13\r\nServer: test\r\n\r\n";
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( list_subprotocol_empty ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\nSec-WebSocket-Protocol: foo\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: test\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
std::string subprotocol;
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
s.set_open_handler(bind(&open_func_subprotocol,&s,&subprotocol,::_1));
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
BOOST_CHECK_EQUAL(subprotocol, "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( list_subprotocol_one ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\nSec-WebSocket-Protocol: foo\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: test\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
std::string validate;
|
||||
std::string open;
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
s.set_validate_handler(bind(&validate_func_subprotocol,&s,&validate,"",::_1));
|
||||
s.set_open_handler(bind(&open_func_subprotocol,&s,&open,::_1));
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
BOOST_CHECK_EQUAL(validate, "foo,");
|
||||
BOOST_CHECK_EQUAL(open, "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( accept_subprotocol_one ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\nSec-WebSocket-Protocol: foo\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nSec-WebSocket-Protocol: foo\r\nServer: test\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
std::string validate;
|
||||
std::string open;
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
s.set_validate_handler(bind(&validate_func_subprotocol,&s,&validate,"foo",::_1));
|
||||
s.set_open_handler(bind(&open_func_subprotocol,&s,&open,::_1));
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
BOOST_CHECK_EQUAL(validate, "foo,");
|
||||
BOOST_CHECK_EQUAL(open, "foo");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( accept_subprotocol_invalid ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\nSec-WebSocket-Protocol: foo\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nSec-WebSocket-Protocol: foo\r\nServer: test\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
std::string validate;
|
||||
std::string open;
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
s.set_validate_handler(bind(&validate_func_subprotocol,&s,&validate,"foo2",::_1));
|
||||
s.set_open_handler(bind(&open_func_subprotocol,&s,&open,::_1));
|
||||
|
||||
std::string o;
|
||||
|
||||
BOOST_CHECK_THROW(o = run_server_test(s,input), websocketpp::lib::error_code);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( accept_subprotocol_two ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\nSec-WebSocket-Protocol: foo, bar\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nSec-WebSocket-Protocol: bar\r\nServer: test\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
std::string validate;
|
||||
std::string open;
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
s.set_validate_handler(bind(&validate_func_subprotocol,&s,&validate,"bar",::_1));
|
||||
s.set_open_handler(bind(&open_func_subprotocol,&s,&open,::_1));
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
BOOST_CHECK_EQUAL(validate, "foo,bar,");
|
||||
BOOST_CHECK_EQUAL(open, "bar");
|
||||
}
|
||||
|
||||
/*BOOST_AUTO_TEST_CASE( user_reject_origin ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example2.com\r\n\r\n";
|
||||
std::string output = "HTTP/1.1 403 Forbidden\r\nServer: test\r\n\r\n";
|
||||
|
||||
server s;
|
||||
s.set_user_agent("test");
|
||||
|
||||
BOOST_CHECK(run_server_test(s,input) == output);
|
||||
}*/
|
||||
|
||||
@@ -65,25 +65,25 @@ BOOST_AUTO_TEST_CASE( value_extraction ) {
|
||||
|
||||
// Value = 1000
|
||||
payload[0] = 0x03;
|
||||
payload[1] = 0xe8;
|
||||
payload[1] = char(0xe8);
|
||||
BOOST_CHECK( close::extract_code(payload,ec) == close::status::normal );
|
||||
BOOST_CHECK( !ec );
|
||||
|
||||
// Value = 1004
|
||||
payload[0] = 0x03;
|
||||
payload[1] = 0xec;
|
||||
payload[1] = char(0xec);
|
||||
BOOST_CHECK( close::extract_code(payload,ec) == 1004 );
|
||||
BOOST_CHECK( ec == error::reserved_close_code );
|
||||
|
||||
// Value = 1005
|
||||
payload[0] = 0x03;
|
||||
payload[1] = 0xed;
|
||||
payload[1] = char(0xed);
|
||||
BOOST_CHECK( close::extract_code(payload,ec) == close::status::no_status );
|
||||
BOOST_CHECK( ec == error::invalid_close_code );
|
||||
|
||||
// Value = 3000
|
||||
payload[0] = 0x0b;
|
||||
payload[1] = 0xb8;
|
||||
payload[1] = char(0xb8);
|
||||
BOOST_CHECK( close::extract_code(payload,ec) == 3000 );
|
||||
BOOST_CHECK( !ec );
|
||||
}
|
||||
@@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE( extract_reason ) {
|
||||
BOOST_CHECK( !ec );
|
||||
|
||||
payload = "000";
|
||||
payload[2] = 0xFF;
|
||||
payload[2] = char(0xFF);
|
||||
|
||||
close::extract_reason(payload,ec);
|
||||
BOOST_CHECK( ec == error::invalid_utf8 );
|
||||
|
||||
@@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE( prepare_masking_key2 ) {
|
||||
|
||||
// TODO: figure out a way to run/test both 4 and 8 byte versions.
|
||||
BOOST_AUTO_TEST_CASE( circshift ) {
|
||||
if (sizeof(size_t) == 8) {
|
||||
/*if (sizeof(size_t) == 8) {
|
||||
size_t test = 0x0123456789abcdef;
|
||||
|
||||
BOOST_CHECK( frame::circshift_prepared_key(test,0) == 0x0123456789abcdef);
|
||||
@@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE( circshift ) {
|
||||
BOOST_CHECK( frame::circshift_prepared_key(test,1) == 0x67012345);
|
||||
BOOST_CHECK( frame::circshift_prepared_key(test,2) == 0x45670123);
|
||||
BOOST_CHECK( frame::circshift_prepared_key(test,3) == 0x23456701);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( block_byte_mask ) {
|
||||
@@ -392,12 +392,14 @@ BOOST_AUTO_TEST_CASE( continuous_word_mask ) {
|
||||
size_t pkey,pkey_temp;
|
||||
pkey = frame::prepare_masking_key(key);
|
||||
std::fill_n(input,16,0x00);
|
||||
std::fill_n(output,16,0x00);
|
||||
frame::word_mask_circ(input,output,15,pkey);
|
||||
BOOST_CHECK( std::equal(output,output+16,masked) );
|
||||
|
||||
// calls not split on word boundaries
|
||||
pkey = frame::prepare_masking_key(key);
|
||||
std::fill_n(input,16,0x00);
|
||||
std::fill_n(output,16,0x00);
|
||||
|
||||
pkey_temp = frame::word_mask_circ(input,output,7,pkey);
|
||||
BOOST_CHECK( std::equal(output,output+7,masked) );
|
||||
@@ -462,4 +464,4 @@ BOOST_AUTO_TEST_CASE( continuous_word_mask2 ) {
|
||||
pkey = frame::prepare_masking_key(key);
|
||||
frame::word_mask_circ(buffer,12,pkey);
|
||||
BOOST_CHECK( std::equal(buffer,buffer+12,unmasked) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ BOOST_AUTO_TEST_CASE( uri_valid_2 ) {
|
||||
BOOST_CHECK( uri.get_secure() == true );
|
||||
BOOST_CHECK( uri.get_host() == "thor-websocket.zaphoyd.net");
|
||||
BOOST_CHECK( uri.get_port() == 88 );
|
||||
BOOST_CHECK( uri.get_resource() == "/" );
|
||||
BOOST_CHECK( uri.get_resource() == "/" );
|
||||
} catch (websocketpp::uri_exception&) {
|
||||
exception = true;
|
||||
}
|
||||
@@ -153,11 +153,11 @@ BOOST_AUTO_TEST_CASE( uri_invalid_long_port ) {
|
||||
BOOST_CHECK( exception == true);
|
||||
}
|
||||
|
||||
// Invalid URI (http method)
|
||||
BOOST_AUTO_TEST_CASE( uri_invalid_http ) {
|
||||
// Invalid URI (bogus scheme method)
|
||||
BOOST_AUTO_TEST_CASE( uri_invalid_scheme ) {
|
||||
bool exception = false;
|
||||
try {
|
||||
websocketpp::uri uri("http://localhost:9000/chat");
|
||||
websocketpp::uri uri("foo://localhost:9000/chat");
|
||||
} catch (websocketpp::uri_exception&) {
|
||||
exception = true;
|
||||
}
|
||||
@@ -165,6 +165,23 @@ BOOST_AUTO_TEST_CASE( uri_invalid_http ) {
|
||||
BOOST_CHECK( exception == true);
|
||||
}
|
||||
|
||||
// Valid URI (http method)
|
||||
BOOST_AUTO_TEST_CASE( uri_http_scheme ) {
|
||||
bool exception = false;
|
||||
try {
|
||||
websocketpp::uri uri("http://localhost:9000/chat");
|
||||
|
||||
BOOST_CHECK( uri.get_secure() == false );
|
||||
BOOST_CHECK( uri.get_host() == "localhost");
|
||||
BOOST_CHECK( uri.get_port() == 9000 );
|
||||
BOOST_CHECK( uri.get_resource() == "/chat" );
|
||||
} catch (websocketpp::uri_exception&) {
|
||||
exception = true;
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false);
|
||||
}
|
||||
|
||||
// Valid URI IPv4 literal
|
||||
BOOST_AUTO_TEST_CASE( uri_valid_ipv4_literal ) {
|
||||
bool exception = false;
|
||||
|
||||
@@ -56,4 +56,12 @@ BOOST_AUTO_TEST_CASE( substr_not_found ) {
|
||||
BOOST_CHECK(websocketpp::utility::ci_find_substr(haystack,needle) == haystack.end());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( string_replace_all ) {
|
||||
std::string source = "foo \"bar\" baz";
|
||||
std::string dest = "foo \\\"bar\\\" baz";
|
||||
|
||||
using websocketpp::utility::string_replace_all;
|
||||
BOOST_CHECK_EQUAL(string_replace_all(source,"\"","\\\""),dest);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/*
|
||||
******
|
||||
base64.hpp is a repackaging of the base64.cpp and base64.h files into a single header
|
||||
suitable for use as a header only library. This conversion was done by Peter Thorson
|
||||
(webmaster@zaphoyd.com) in 2012. All modifications to the code are redistributed under
|
||||
the same license as the original, which is listed below.
|
||||
******
|
||||
******
|
||||
base64.hpp is a repackaging of the base64.cpp and base64.h files into a
|
||||
single headersuitable for use as a header only library. This conversion was
|
||||
done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to
|
||||
the code are redistributed under the same license as the original, which is
|
||||
listed below.
|
||||
******
|
||||
|
||||
base64.cpp and base64.h
|
||||
|
||||
@@ -36,7 +37,6 @@
|
||||
#define _BASE64_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
@@ -44,7 +44,10 @@ static const std::string base64_chars =
|
||||
"0123456789+/";
|
||||
|
||||
static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
return (c == 43 || // +
|
||||
(c >= 47 && c <= 57) || // /-9
|
||||
(c >= 65 && c <= 90) || // A-Z
|
||||
(c >= 97 && c <= 122)); // a-z
|
||||
}
|
||||
|
||||
inline std::string base64_encode(unsigned char const* bytes_to_encode, unsigned
|
||||
@@ -97,6 +100,10 @@ inline std::string base64_encode(unsigned char const* bytes_to_encode, unsigned
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string base64_encode(const std::string & data) {
|
||||
return base64_encode(reinterpret_cast<const unsigned char *>(data.data()),data.size());
|
||||
}
|
||||
|
||||
inline std::string base64_decode(std::string const& encoded_string) {
|
||||
size_t in_len = encoded_string.size();
|
||||
int i = 0;
|
||||
@@ -134,10 +141,12 @@ inline std::string base64_decode(std::string const& encoded_string) {
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||
for (j = 0; (j < i - 1); j++) {
|
||||
ret += static_cast<std::string::value_type>(char_array_3[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // _BASE64_HPP_
|
||||
#endif // _BASE64_HPP_
|
||||
|
||||
@@ -40,6 +40,12 @@ namespace websocketpp {
|
||||
namespace lib {
|
||||
namespace net {
|
||||
|
||||
inline bool is_little_endian() {
|
||||
short int val = 0x1;
|
||||
char *ptr = (char*)&val;
|
||||
return (ptr[0] == 1);
|
||||
}
|
||||
|
||||
#define TYP_INIT 0
|
||||
#define TYP_SMLE 1
|
||||
#define TYP_BIGE 2
|
||||
@@ -56,7 +62,7 @@ inline uint64_t htonll(uint64_t src) {
|
||||
typ = (x.c[7] == 0x01ULL) ? TYP_BIGE : TYP_SMLE;
|
||||
}
|
||||
if (typ == TYP_BIGE)
|
||||
return src;
|
||||
return src;
|
||||
x.ull = src;
|
||||
c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c;
|
||||
c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c;
|
||||
|
||||
@@ -49,9 +49,11 @@ namespace lib {
|
||||
#ifdef _WEBSOCKETPP_CPP11_THREAD_
|
||||
using std::mutex;
|
||||
using std::lock_guard;
|
||||
using std::thread;
|
||||
#else
|
||||
using boost::mutex;
|
||||
using boost::lock_guard;
|
||||
using boost::thread;
|
||||
#endif
|
||||
|
||||
} // namespace lib
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct asio_tls : public core {
|
||||
typedef asio_tls type;
|
||||
|
||||
typedef core::concurrency_type concurrency_type;
|
||||
|
||||
typedef core::request_type request_type;
|
||||
@@ -55,9 +57,11 @@ struct asio_tls : public core {
|
||||
typedef core::rng_type rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef asio_tls::concurrency_type concurrency_type;
|
||||
typedef asio_tls::alog_type alog_type;
|
||||
typedef asio_tls::elog_type elog_type;
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct asio_tls : public core_client {
|
||||
typedef asio_tls type;
|
||||
|
||||
typedef core_client::concurrency_type concurrency_type;
|
||||
|
||||
typedef core_client::request_type request_type;
|
||||
@@ -55,9 +57,11 @@ struct asio_tls : public core_client {
|
||||
typedef core_client::rng_type rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef asio_tls::concurrency_type concurrency_type;
|
||||
typedef asio_tls::alog_type alog_type;
|
||||
typedef asio_tls::elog_type elog_type;
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct asio : public core {
|
||||
typedef asio type;
|
||||
|
||||
typedef core::concurrency_type concurrency_type;
|
||||
|
||||
typedef core::request_type request_type;
|
||||
@@ -50,9 +52,11 @@ struct asio : public core {
|
||||
typedef core::rng_type rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef asio::concurrency_type concurrency_type;
|
||||
typedef asio::alog_type alog_type;
|
||||
typedef asio::elog_type elog_type;
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
typedef websocketpp::transport::asio::basic_socket::endpoint
|
||||
socket_type;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct asio_client : public core_client {
|
||||
typedef asio_client type;
|
||||
|
||||
typedef core_client::concurrency_type concurrency_type;
|
||||
|
||||
typedef core_client::request_type request_type;
|
||||
@@ -50,9 +52,11 @@ struct asio_client : public core_client {
|
||||
typedef core_client::rng_type rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef asio_client::concurrency_type concurrency_type;
|
||||
typedef asio_client::alog_type alog_type;
|
||||
typedef asio_client::elog_type elog_type;
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
typedef websocketpp::transport::asio::basic_socket::endpoint
|
||||
socket_type;
|
||||
};
|
||||
|
||||
@@ -59,12 +59,14 @@
|
||||
#include <websocketpp/connection_base.hpp>
|
||||
|
||||
// Extensions
|
||||
#include <websocketpp/extensions/permessage_compress/disabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct core {
|
||||
typedef core type;
|
||||
|
||||
// Concurrency policy
|
||||
typedef websocketpp::concurrency::basic concurrency_type;
|
||||
|
||||
@@ -90,9 +92,11 @@ struct core {
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef core::concurrency_type concurrency_type;
|
||||
typedef core::elog_type elog_type;
|
||||
typedef core::alog_type alog_type;
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
};
|
||||
|
||||
/// Transport Endpoint Component
|
||||
@@ -111,7 +115,33 @@ struct core {
|
||||
* recommended.
|
||||
*/
|
||||
static const int client_version = 13; // RFC6455
|
||||
|
||||
/// Default static error logging channels
|
||||
/**
|
||||
* Which error logging channels to enable at compile time. Channels not
|
||||
* enabled here will be unable to be selected by programs using the library.
|
||||
* This option gives an optimizing compiler the ability to remove entirely
|
||||
* code to test whether or not to print out log messages on a certain
|
||||
* channel
|
||||
*
|
||||
* Default is all except for development/debug level errors
|
||||
*/
|
||||
static const websocketpp::log::level elog_level =
|
||||
websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel;
|
||||
|
||||
/// Default static access logging channels
|
||||
/**
|
||||
* Which access logging channels to enable at compile time. Channels not
|
||||
* enabled here will be unable to be selected by programs using the library.
|
||||
* This option gives an optimizing compiler the ability to remove entirely
|
||||
* code to test whether or not to print out log messages on a certain
|
||||
* channel
|
||||
*
|
||||
* Default is all except for development/debug level access messages
|
||||
*/
|
||||
static const websocketpp::log::level alog_level =
|
||||
websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel;
|
||||
|
||||
///
|
||||
static const size_t connection_read_buffer_size = 512;
|
||||
|
||||
@@ -145,7 +175,7 @@ struct core {
|
||||
/// Extension specific settings:
|
||||
|
||||
/// permessage_compress extension
|
||||
struct permessage_compress_config {
|
||||
struct permessage_deflate_config {
|
||||
typedef core::request_type request_type;
|
||||
|
||||
/// If the remote endpoint requests that we reset the compression
|
||||
@@ -160,14 +190,14 @@ struct core {
|
||||
static const uint8_t minimum_outgoing_window_bits = 8;
|
||||
};
|
||||
|
||||
typedef websocketpp::extensions::permessage_compress::disabled
|
||||
<permessage_compress_config> permessage_compress_type;
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
/// Autonegotiate permessage-compress
|
||||
/// Autonegotiate permessage-deflate
|
||||
/**
|
||||
* Automatically enables the permessage-compress extension.
|
||||
* Automatically enables the permessage-deflate extension.
|
||||
*
|
||||
* For clients this results in a permessage-compress extension request being
|
||||
* For clients this results in a permessage-deflate extension request being
|
||||
* sent with every request rather than requiring it to be requested manually
|
||||
*
|
||||
* For servers this results in accepting the first set of extension settings
|
||||
|
||||
@@ -57,12 +57,14 @@
|
||||
#include <websocketpp/connection_base.hpp>
|
||||
|
||||
// Extensions
|
||||
#include <websocketpp/extensions/permessage_compress/disabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct core_client {
|
||||
typedef core_client type;
|
||||
|
||||
// Concurrency policy
|
||||
typedef websocketpp::concurrency::basic concurrency_type;
|
||||
|
||||
@@ -89,9 +91,11 @@ struct core_client {
|
||||
concurrency_type> rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef core_client::concurrency_type concurrency_type;
|
||||
typedef core_client::elog_type elog_type;
|
||||
typedef core_client::alog_type alog_type;
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
};
|
||||
|
||||
/// Transport Endpoint Component
|
||||
@@ -110,7 +114,33 @@ struct core_client {
|
||||
* recommended.
|
||||
*/
|
||||
static const int client_version = 13; // RFC6455
|
||||
|
||||
/// Default static error logging channels
|
||||
/**
|
||||
* Which error logging channels to enable at compile time. Channels not
|
||||
* enabled here will be unable to be selected by programs using the library.
|
||||
* This option gives an optimizing compiler the ability to remove entirely
|
||||
* code to test whether or not to print out log messages on a certain
|
||||
* channel
|
||||
*
|
||||
* Default is all except for development/debug level errors
|
||||
*/
|
||||
static const websocketpp::log::level elog_level =
|
||||
websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel;
|
||||
|
||||
/// Default static access logging channels
|
||||
/**
|
||||
* Which access logging channels to enable at compile time. Channels not
|
||||
* enabled here will be unable to be selected by programs using the library.
|
||||
* This option gives an optimizing compiler the ability to remove entirely
|
||||
* code to test whether or not to print out log messages on a certain
|
||||
* channel
|
||||
*
|
||||
* Default is all except for development/debug level access messages
|
||||
*/
|
||||
static const websocketpp::log::level alog_level =
|
||||
websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel;
|
||||
|
||||
///
|
||||
static const size_t connection_read_buffer_size = 512;
|
||||
|
||||
@@ -143,8 +173,8 @@ struct core_client {
|
||||
|
||||
/// Extension specific settings:
|
||||
|
||||
/// permessage_compress extension
|
||||
struct permessage_compress_config {
|
||||
/// permessage_deflate extension
|
||||
struct permessage_deflate_config {
|
||||
typedef core_client::request_type request_type;
|
||||
|
||||
/// If the remote endpoint requests that we reset the compression
|
||||
@@ -159,8 +189,8 @@ struct core_client {
|
||||
static const uint8_t minimum_outgoing_window_bits = 8;
|
||||
};
|
||||
|
||||
typedef websocketpp::extensions::permessage_compress::disabled
|
||||
<permessage_compress_config> permessage_compress_type;
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
/// Autonegotiate permessage-compress
|
||||
/**
|
||||
|
||||
215
websocketpp/config/debug.hpp
Normal file
215
websocketpp/config/debug.hpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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_CONFIG_DEBUG_HPP
|
||||
#define WEBSOCKETPP_CONFIG_DEBUG_HPP
|
||||
|
||||
|
||||
|
||||
// Non-Policy common stuff
|
||||
#include <websocketpp/common/cpp11.hpp>
|
||||
#include <websocketpp/common/stdint.hpp>
|
||||
|
||||
// Concurrency
|
||||
#include <websocketpp/concurrency/basic.hpp>
|
||||
|
||||
// Transport
|
||||
#include <websocketpp/transport/iostream/endpoint.hpp>
|
||||
|
||||
// HTTP
|
||||
#include <websocketpp/http/request.hpp>
|
||||
#include <websocketpp/http/response.hpp>
|
||||
|
||||
// Messages
|
||||
#include <websocketpp/message_buffer/message.hpp>
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
|
||||
// Loggers
|
||||
#include <websocketpp/logger/basic.hpp>
|
||||
|
||||
// RNG
|
||||
#include <websocketpp/random/none.hpp>
|
||||
|
||||
// User stub base classes
|
||||
#include <websocketpp/endpoint_base.hpp>
|
||||
#include <websocketpp/connection_base.hpp>
|
||||
|
||||
// Extensions
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct debug_core {
|
||||
typedef debug_core type;
|
||||
|
||||
// Concurrency policy
|
||||
typedef websocketpp::concurrency::basic concurrency_type;
|
||||
|
||||
// HTTP Parser Policies
|
||||
typedef http::parser::request request_type;
|
||||
typedef http::parser::response response_type;
|
||||
|
||||
// Message Policies
|
||||
typedef message_buffer::message<message_buffer::alloc::con_msg_manager>
|
||||
message_type;
|
||||
typedef message_buffer::alloc::con_msg_manager<message_type>
|
||||
con_msg_manager_type;
|
||||
typedef message_buffer::alloc::endpoint_msg_manager<con_msg_manager_type>
|
||||
endpoint_msg_manager_type;
|
||||
|
||||
/// Logging policies
|
||||
typedef websocketpp::log::basic<concurrency_type,
|
||||
websocketpp::log::elevel> elog_type;
|
||||
typedef websocketpp::log::basic<concurrency_type,
|
||||
websocketpp::log::alevel> alog_type;
|
||||
|
||||
/// RNG policies
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
};
|
||||
|
||||
/// Transport Endpoint Component
|
||||
typedef websocketpp::transport::iostream::endpoint<transport_config>
|
||||
transport_type;
|
||||
|
||||
/// User overridable Endpoint base class
|
||||
typedef websocketpp::endpoint_base endpoint_base;
|
||||
/// User overridable Connection base class
|
||||
typedef websocketpp::connection_base connection_base;
|
||||
|
||||
/// WebSocket Protocol version to use as a client
|
||||
/**
|
||||
* What version of the WebSocket Protocol to use for outgoing client
|
||||
* connections. Setting this to a value other than 13 (RFC6455) is not
|
||||
* recommended.
|
||||
*/
|
||||
static const int client_version = 13; // RFC6455
|
||||
|
||||
/// Default static error logging channels
|
||||
/**
|
||||
* Which error logging channels to enable at compile time. Channels not
|
||||
* enabled here will be unable to be selected by programs using the library.
|
||||
* This option gives an optimizing compiler the ability to remove entirely
|
||||
* code to test whether or not to print out log messages on a certain
|
||||
* channel
|
||||
*
|
||||
* Default is all except for development/debug level errors
|
||||
*/
|
||||
static const websocketpp::log::level elog_level =
|
||||
websocketpp::log::elevel::all;
|
||||
|
||||
/// Default static access logging channels
|
||||
/**
|
||||
* Which access logging channels to enable at compile time. Channels not
|
||||
* enabled here will be unable to be selected by programs using the library.
|
||||
* This option gives an optimizing compiler the ability to remove entirely
|
||||
* code to test whether or not to print out log messages on a certain
|
||||
* channel
|
||||
*
|
||||
* Default is all except for development/debug level access messages
|
||||
*/
|
||||
static const websocketpp::log::level alog_level =
|
||||
websocketpp::log::alevel::all;
|
||||
|
||||
///
|
||||
static const size_t connection_read_buffer_size = 512;
|
||||
|
||||
/// Drop connections immediately on protocol error.
|
||||
/**
|
||||
* Drop connections on protocol error rather than sending a close frame.
|
||||
* Off by default. This may result in legit messages near the error being
|
||||
* dropped as well. It may free up resources otherwise spent dealing with
|
||||
* misbehaving clients.
|
||||
*/
|
||||
static const bool drop_on_protocol_error = false;
|
||||
|
||||
/// Suppresses the return of detailed connection close information
|
||||
/**
|
||||
* Silence close suppresses the return of detailed connection close
|
||||
* information during the closing handshake. This information is useful
|
||||
* for debugging and presenting useful errors to end users but may be
|
||||
* undesirable for security reasons in some production environments.
|
||||
* Close reasons could be used by an attacker to confirm that the endpoint
|
||||
* is out of resources or be used to identify the WebSocket implimentation
|
||||
* in use.
|
||||
*
|
||||
* Note: this will suppress *all* close codes, including those explicitly
|
||||
* sent by local applications.
|
||||
*/
|
||||
static const bool silent_close = false;
|
||||
|
||||
/// Global flag for enabling/disabling extensions
|
||||
static const bool enable_extensions = true;
|
||||
|
||||
/// Extension specific settings:
|
||||
|
||||
/// permessage_compress extension
|
||||
struct permessage_deflate_config {
|
||||
typedef type::request_type request_type;
|
||||
|
||||
/// If the remote endpoint requests that we reset the compression
|
||||
/// context after each message should we honor the request?
|
||||
static const bool allow_disabling_context_takeover = true;
|
||||
|
||||
/// If the remote endpoint requests that we reduce the size of the
|
||||
/// LZ77 sliding window size this is the lowest value that will be
|
||||
/// allowed. Values range from 8 to 15. A value of 8 means we will
|
||||
/// allow any possible window size. A value of 15 means do not allow
|
||||
/// negotiation of the window size (ie require the default).
|
||||
static const uint8_t minimum_outgoing_window_bits = 8;
|
||||
};
|
||||
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
/// Autonegotiate permessage-deflate
|
||||
/**
|
||||
* Automatically enables the permessage-deflate extension.
|
||||
*
|
||||
* For clients this results in a permessage-deflate extension request being
|
||||
* sent with every request rather than requiring it to be requested manually
|
||||
*
|
||||
* For servers this results in accepting the first set of extension settings
|
||||
* requested by the client that we understand being used. The alternative is
|
||||
* requiring the extension to be manually negotiated in `validate`. With
|
||||
* auto-negotiate on, you may still override the auto-negotiate manually if
|
||||
* needed.
|
||||
*/
|
||||
//static const bool autonegotiate_compression = false;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_CONFIG_CORE_HPP
|
||||
76
websocketpp/config/debug_asio.hpp
Normal file
76
websocketpp/config/debug_asio.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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_CONFIG_ASIO_TLS_DEBUG_HPP
|
||||
#define WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP
|
||||
|
||||
#include <websocketpp/config/debug.hpp>
|
||||
#include <websocketpp/transport/asio/endpoint.hpp>
|
||||
#include <websocketpp/transport/asio/security/tls.hpp>
|
||||
|
||||
// Pull in non-tls config
|
||||
#include <websocketpp/config/debug_asio_no_tls.hpp>
|
||||
|
||||
// Define TLS config
|
||||
namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct debug_asio_tls : public debug_core {
|
||||
typedef debug_asio_tls type;
|
||||
typedef debug_core base;
|
||||
|
||||
typedef base::concurrency_type concurrency_type;
|
||||
|
||||
typedef base::request_type request_type;
|
||||
typedef base::response_type response_type;
|
||||
|
||||
typedef base::message_type message_type;
|
||||
typedef base::con_msg_manager_type con_msg_manager_type;
|
||||
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
|
||||
|
||||
typedef base::alog_type alog_type;
|
||||
typedef base::elog_type elog_type;
|
||||
|
||||
typedef base::rng_type rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::transport::asio::endpoint<transport_config>
|
||||
transport_type;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP
|
||||
72
websocketpp/config/debug_asio_no_tls.hpp
Normal file
72
websocketpp/config/debug_asio_no_tls.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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_CONFIG_ASIO_DEBUG_HPP
|
||||
#define WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP
|
||||
|
||||
#include <websocketpp/config/debug.hpp>
|
||||
#include <websocketpp/transport/asio/endpoint.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace config {
|
||||
|
||||
struct debug_asio : public debug_core {
|
||||
typedef debug_asio type;
|
||||
typedef debug_core base;
|
||||
|
||||
typedef base::concurrency_type concurrency_type;
|
||||
|
||||
typedef base::request_type request_type;
|
||||
typedef base::response_type response_type;
|
||||
|
||||
typedef base::message_type message_type;
|
||||
typedef base::con_msg_manager_type con_msg_manager_type;
|
||||
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
|
||||
|
||||
typedef base::alog_type alog_type;
|
||||
typedef base::elog_type elog_type;
|
||||
|
||||
typedef base::rng_type rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
typedef websocketpp::transport::asio::basic_socket::endpoint
|
||||
socket_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::transport::asio::endpoint<transport_config>
|
||||
transport_type;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP
|
||||
@@ -39,11 +39,11 @@
|
||||
#include <websocketpp/processors/processor.hpp>
|
||||
#include <websocketpp/transport/base/connection.hpp>
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
@@ -73,12 +73,11 @@ typedef lib::function<void(connection_hdl)> http_handler;
|
||||
namespace session {
|
||||
namespace state {
|
||||
// externally visible session state (states based on the RFC)
|
||||
|
||||
enum value {
|
||||
CONNECTING = 0,
|
||||
OPEN = 1,
|
||||
CLOSING = 2,
|
||||
CLOSED = 3
|
||||
connecting = 0,
|
||||
open = 1,
|
||||
closing = 2,
|
||||
closed = 3
|
||||
};
|
||||
} // namespace state
|
||||
|
||||
@@ -172,7 +171,7 @@ public:
|
||||
elog_type& elog, rng_type & rng)
|
||||
: transport_con_type(is_server,alog,elog)
|
||||
, m_user_agent(ua)
|
||||
, m_state(session::state::CONNECTING)
|
||||
, m_state(session::state::connecting)
|
||||
, m_internal_state(session::internal_state::USER_INIT)
|
||||
, m_msg_manager(new con_msg_manager_type())
|
||||
, m_send_buffer_size(0)
|
||||
@@ -181,6 +180,8 @@ public:
|
||||
, m_alog(alog)
|
||||
, m_elog(elog)
|
||||
, m_rng(rng)
|
||||
, m_local_close_code(close::status::abnormal_close)
|
||||
, m_remote_close_code(close::status::abnormal_close)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"connection constructor");
|
||||
}
|
||||
@@ -519,11 +520,107 @@ public:
|
||||
* @param uri The new URI to set
|
||||
*/
|
||||
void set_uri(uri_ptr uri);
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Subprotocol negotiation //
|
||||
/////////////////////////////
|
||||
|
||||
/// Gets the negotated subprotocol
|
||||
/**
|
||||
* Retrieves the subprotocol that was negotiated during the handshake. This
|
||||
* method is valid in the open handler and later.
|
||||
*
|
||||
* @return The negotiated subprotocol
|
||||
*/
|
||||
const std::string& get_subprotocol() const;
|
||||
|
||||
/// Gets all of the subprotocols requested by the client
|
||||
/**
|
||||
* Retrieves the subprotocols that were requested during the handshake. This
|
||||
* method is valid in the validate handler and later.
|
||||
*
|
||||
* @return A vector of the requested subprotocol
|
||||
*/
|
||||
const std::vector<std::string> & get_requested_subprotocols() const;
|
||||
|
||||
/// Adds the given subprotocol string to the request list (exception free)
|
||||
/**
|
||||
* Adds a subprotocol to the list to send with the opening handshake. This
|
||||
* may be called multiple times to request more than one. If the server
|
||||
* supports one of these, it may choose one. If so, it will return it
|
||||
* in it's handshake reponse and the value will be available via
|
||||
* get_subprotocol(). Subprotocol requests should be added in order of
|
||||
* preference.
|
||||
*
|
||||
* @param request The subprotocol to request
|
||||
*
|
||||
* @param ec A reference to an error code that will be filled in the case of
|
||||
* errors
|
||||
*/
|
||||
void add_subprotocol(const std::string &request, lib::error_code & ec);
|
||||
|
||||
/// Adds the given subprotocol string to the request list
|
||||
/**
|
||||
* Adds a subprotocol to the list to send with the opening handshake. This
|
||||
* may be called multiple times to request more than one. If the server
|
||||
* supports one of these, it may choose one. If so, it will return it
|
||||
* in it's handshake reponse and the value will be available via
|
||||
* get_subprotocol(). Subprotocol requests should be added in order of
|
||||
* preference.
|
||||
*
|
||||
* @param request The subprotocol to request
|
||||
*/
|
||||
void add_subprotocol(const std::string &request);
|
||||
|
||||
/// Select a subprotocol to use (exception free)
|
||||
/**
|
||||
* Indicates which subprotocol should be used for this connection. Valid
|
||||
* only during the validate handler callback. Subprotocol selected must have
|
||||
* been requested by the client. Consult get_requested_subprotocols() for a
|
||||
* list of valid subprotocols.
|
||||
*
|
||||
* This member function is valid on server endpoints/connections only
|
||||
*
|
||||
* @param value The subprotocol to select
|
||||
*
|
||||
* @param ec A reference to an error code that will be filled in the case of
|
||||
* errors
|
||||
*/
|
||||
void select_subprotocol(const std::string & value, lib::error_code & ec);
|
||||
|
||||
/// Select a subprotocol to use
|
||||
/**
|
||||
* Indicates which subprotocol should be used for this connection. Valid
|
||||
* only during the validate handler callback. Subprotocol selected must have
|
||||
* been requested by the client. Consult get_requested_subprotocols() for a
|
||||
* list of valid subprotocols.
|
||||
*
|
||||
* This member function is valid on server endpoints/connections only
|
||||
*
|
||||
* @param value The subprotocol to select
|
||||
*/
|
||||
void select_subprotocol(const std::string & value);
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Pass-through access to the request and response objects //
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
/// Retrieve a request header
|
||||
/**
|
||||
* Retrieve the value of a header from the handshake HTTP request.
|
||||
*
|
||||
* @param key Name of the header to get
|
||||
*/
|
||||
const std::string & get_request_header(const std::string &key);
|
||||
|
||||
/// Retrieve a response header
|
||||
/**
|
||||
* Retrieve the value of a header from the handshake HTTP request.
|
||||
*
|
||||
* @param key Name of the header to get
|
||||
*/
|
||||
const std::string & get_response_header(const std::string &key);
|
||||
|
||||
/// Set response status code and message
|
||||
/**
|
||||
* Sets the response status code to `code` and looks up the corresponding
|
||||
@@ -646,6 +743,14 @@ public:
|
||||
*/
|
||||
const std::string& get_origin() const;
|
||||
|
||||
/// Return the connection state.
|
||||
/**
|
||||
* Values can be connecting, open, closing, and closed
|
||||
*
|
||||
* @return The connection's current state.
|
||||
*/
|
||||
session::state::value get_state() const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// The remaining public member functions are for internal/policy use //
|
||||
// only. Do not call from application code unless you understand what //
|
||||
@@ -864,6 +969,13 @@ private:
|
||||
*/
|
||||
void log_open_result();
|
||||
|
||||
/// Prints information about a connection being closed to the access log
|
||||
/**
|
||||
* Prints information about a connection being closed to the access log.
|
||||
* Includes: local and remote close codes and reasons
|
||||
*/
|
||||
void log_close_result();
|
||||
|
||||
// static settings
|
||||
const std::string m_user_agent;
|
||||
|
||||
@@ -942,6 +1054,10 @@ private:
|
||||
*/
|
||||
std::vector<transport::buffer> m_send_buffer;
|
||||
|
||||
/// a pointer to hold on to the current message being written to keep it
|
||||
/// from going out of scope before the write is complete.
|
||||
message_ptr m_current_msg;
|
||||
|
||||
/// True if there is currently an outstanding transport write
|
||||
/**
|
||||
* Lock m_write_lock
|
||||
@@ -952,6 +1068,11 @@ private:
|
||||
request_type m_request;
|
||||
response_type m_response;
|
||||
uri_ptr m_uri;
|
||||
std::string m_subprotocol;
|
||||
|
||||
// connection data that might not be necessary to keep around for the life
|
||||
// of the whole connection.
|
||||
std::vector<std::string> m_requested_subprotocols;
|
||||
|
||||
const bool m_is_server;
|
||||
alog_type& m_alog;
|
||||
|
||||
@@ -89,12 +89,13 @@ public:
|
||||
typedef lib::shared_ptr<connection_weak_ptr> hdl_type;
|
||||
|
||||
explicit endpoint(bool is_server)
|
||||
: m_elog(&std::cerr)
|
||||
: m_alog(config::alog_level,&std::cout)
|
||||
, m_elog(config::elog_level,&std::cerr)
|
||||
, m_user_agent(::websocketpp::user_agent)
|
||||
, m_is_server(is_server)
|
||||
{
|
||||
m_alog.set_channels(0xffffffff);
|
||||
m_elog.set_channels(0xffffffff);
|
||||
m_alog.set_channels(config::alog_level);
|
||||
m_elog.set_channels(config::elog_level);
|
||||
|
||||
m_alog.write(log::alevel::devel,"endpoint constructor");
|
||||
|
||||
@@ -304,13 +305,7 @@ public:
|
||||
*/
|
||||
connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) {
|
||||
scoped_lock_type lock(m_mutex);
|
||||
/** @todo This wont build on windows with null as the second template argument.
|
||||
I tried to look at the boost docs for this and cound not find why the second param is here.
|
||||
Cleanup ifdefs when verified this builds on other systems. */
|
||||
#ifdef WIN32
|
||||
connection_ptr con = lib::static_pointer_cast<connection_type>(
|
||||
#else
|
||||
connection_ptr con = lib::static_pointer_cast<connection_type,void>(
|
||||
#endif
|
||||
hdl.lock());
|
||||
if (!con) {
|
||||
|
||||
@@ -61,6 +61,9 @@ enum value {
|
||||
|
||||
/// The endpoint is out of outgoing message buffers
|
||||
no_outgoing_buffers,
|
||||
|
||||
/// The endpoint is out of incoming message buffers
|
||||
no_incoming_buffers,
|
||||
|
||||
/// The connection was in the wrong state for this operation
|
||||
invalid_state,
|
||||
@@ -77,6 +80,9 @@ enum value {
|
||||
/// Invalid UTF-8
|
||||
invalid_utf8,
|
||||
|
||||
/// Invalid subprotocol
|
||||
invalid_subprotocol,
|
||||
|
||||
/// Bad or unknown connection
|
||||
bad_connection,
|
||||
|
||||
@@ -84,7 +90,16 @@ enum value {
|
||||
test,
|
||||
|
||||
/// Connection creation attempted failed
|
||||
con_creation_failed
|
||||
con_creation_failed,
|
||||
|
||||
/// Selected subprotocol was not requested by the client
|
||||
unrequested_subprotocol,
|
||||
|
||||
/// Attempted to use a client specific feature on a server endpoint
|
||||
client_only,
|
||||
|
||||
/// Attempted to use a server specific feature on a client endpoint
|
||||
server_only,
|
||||
}; // enum value
|
||||
|
||||
|
||||
@@ -112,6 +127,8 @@ public:
|
||||
return "invalid uri";
|
||||
case error::no_outgoing_buffers:
|
||||
return "no outgoing message buffers";
|
||||
case error::no_incoming_buffers:
|
||||
return "no incoming message buffers";
|
||||
case error::invalid_state:
|
||||
return "invalid state";
|
||||
case error::bad_close_code:
|
||||
@@ -122,12 +139,20 @@ public:
|
||||
return "Extracted close code is in a reserved range";
|
||||
case error::invalid_utf8:
|
||||
return "Invalid UTF-8";
|
||||
case error::invalid_subprotocol:
|
||||
return "Invalid subprotocol";
|
||||
case error::bad_connection:
|
||||
return "Bad Connection";
|
||||
case error::test:
|
||||
return "Test Error";
|
||||
case error::con_creation_failed:
|
||||
return "Connection creation attempt failed";
|
||||
case error::unrequested_subprotocol:
|
||||
return "Selected subprotocol was not requested by the client";
|
||||
case error::client_only:
|
||||
return "Feature not available on server endpoints";
|
||||
case error::server_only:
|
||||
return "Feature not available on client endpoints";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2013, 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:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2013, 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:
|
||||
@@ -25,8 +25,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETPP_EXTENSION_PERMESSAGE_COMPRESS_DISABLED_HPP
|
||||
#define WEBSOCKETPP_EXTENSION_PERMESSAGE_COMPRESS_DISABLED_HPP
|
||||
#ifndef WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP
|
||||
#define WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP
|
||||
|
||||
#include <websocketpp/common/cpp11.hpp>
|
||||
#include <websocketpp/common/system_error.hpp>
|
||||
@@ -39,12 +39,12 @@
|
||||
|
||||
namespace websocketpp {
|
||||
namespace extensions {
|
||||
namespace permessage_compress {
|
||||
namespace permessage_deflate {
|
||||
|
||||
/// Stub class for use when disabling permessage_compress extension
|
||||
/// Stub class for use when disabling permessage_deflate extension
|
||||
/**
|
||||
* This class is a stub that impliments the permessage_compress interface
|
||||
* with minimal dependencies. It is used to disable permessage_compress
|
||||
* This class is a stub that impliments the permessage_deflate interface
|
||||
* with minimal dependencies. It is used to disable permessage_deflate
|
||||
* functionality at compile time without loading any unnecessary code.
|
||||
*/
|
||||
template <typename config>
|
||||
@@ -58,12 +58,12 @@ public:
|
||||
}
|
||||
|
||||
/// Returns true if the extension is capable of providing
|
||||
/// permessage_compress functionality
|
||||
/// permessage_deflate functionality
|
||||
bool is_implimented() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if permessage_compress functionality is active for this
|
||||
/// Returns true if permessage_deflate functionality is active for this
|
||||
/// connection
|
||||
bool is_enabled() const {
|
||||
return false;
|
||||
@@ -84,8 +84,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace permessage_compress
|
||||
} // namespace permessage_deflate
|
||||
} // namespace extensions
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_EXTENSION_PERMESSAGE_COMPRESS_DISABLED_HPP
|
||||
#endif // WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2013, 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:
|
||||
@@ -25,8 +25,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGECOMPRESS_HPP
|
||||
#define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGECOMPRESS_HPP
|
||||
#ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
|
||||
#define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
|
||||
|
||||
#include <websocketpp/common/cpp11.hpp>
|
||||
#include <websocketpp/common/system_error.hpp>
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
namespace websocketpp {
|
||||
namespace extensions {
|
||||
namespace permessage_compress {
|
||||
namespace permessage_deflate {
|
||||
|
||||
namespace error {
|
||||
enum value {
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
category() {}
|
||||
|
||||
const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
|
||||
return "websocketpp.extension.permessage-compress";
|
||||
return "websocketpp.extension.permessage-deflate";
|
||||
}
|
||||
|
||||
std::string message(int value) const {
|
||||
@@ -107,20 +107,20 @@ lib::error_code make_error_code(error::value e) {
|
||||
}
|
||||
|
||||
} // namespace error
|
||||
} // namespace permessage_compress
|
||||
} // namespace permessage_deflate
|
||||
} // namespace extensions
|
||||
} // namespace websocketpp
|
||||
|
||||
_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
|
||||
template<> struct is_error_code_enum
|
||||
<websocketpp::extensions::permessage_compress::error::value>
|
||||
<websocketpp::extensions::permessage_deflate::error::value>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
|
||||
namespace websocketpp {
|
||||
namespace extensions {
|
||||
namespace permessage_compress {
|
||||
namespace permessage_deflate {
|
||||
|
||||
template <typename config>
|
||||
class method {
|
||||
@@ -404,7 +404,7 @@ private:
|
||||
z_stream m_istate; // inflate state
|
||||
};
|
||||
|
||||
/// Impliments the permessage_compress extension interface
|
||||
/// Impliments the permessage_deflate extension interface
|
||||
/**
|
||||
*
|
||||
* Template parameter econfig defines compile time types, constants, and
|
||||
@@ -422,7 +422,7 @@ private:
|
||||
*
|
||||
*
|
||||
* Methods:
|
||||
* permessage_compress::enabled does not define or impliment any methods
|
||||
* permessage_deflate::enabled does not define or impliment any methods
|
||||
* itself. It uses the attribute list to determine
|
||||
*
|
||||
*
|
||||
@@ -431,6 +431,8 @@ private:
|
||||
*/
|
||||
template <typename econfig>
|
||||
class enabled {
|
||||
typedef std::map<std::string,std::string> string_map;
|
||||
|
||||
typedef typename econfig::request_type::attribute_list attribute_list;
|
||||
typedef typename attribute_list::const_iterator attribute_iterator;
|
||||
typedef lib::shared_ptr< method<econfig> > method_ptr;
|
||||
@@ -442,7 +444,7 @@ class enabled {
|
||||
public:
|
||||
enabled() : m_enabled(false) {}
|
||||
|
||||
/// Attempt to negotiate the permessage_compress extension
|
||||
/// Attempt to negotiate the permessage_deflate extension
|
||||
/**
|
||||
* Parses the attribute list for this extension and attempts to negotiate
|
||||
* the extension. Returns a pair<lib::error_code, std::string>. On success
|
||||
@@ -450,14 +452,41 @@ public:
|
||||
* to return in the handshake response. On error
|
||||
*
|
||||
* @param attributes A list of attributes extracted from the
|
||||
* 'permessage_compress' extension parameter from the original handshake.
|
||||
* 'permessage_deflate' extension parameter from the original handshake.
|
||||
*
|
||||
* @return A pair<lib::error_code, std::string> containing a status code
|
||||
* and a value whose interpretation is dependent on the status code.
|
||||
*/
|
||||
err_str_pair negotiate(const attribute_list& attributes) {
|
||||
err_str_pair negotiate(const string_map& attributes) {
|
||||
err_str_pair ret;
|
||||
|
||||
std::cout << "foo: " << attributes.size() << std::endl;
|
||||
|
||||
string_map::const_iterator it;
|
||||
|
||||
for (it = attributes.begin(); it != attributes.end(); ++it) {
|
||||
std::cout << it->first << ": " << it->second << std::endl;
|
||||
}
|
||||
|
||||
// start by not accepting any parameters
|
||||
if (attributes.size() == 0) {
|
||||
ret.second = "permessage-deflate";
|
||||
} else {
|
||||
ret.first = make_error_code(error::invalid_parameters);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Sec-WebSocket-Extensions: foo; bar; baz=5, foo2; bar2; baz2=5
|
||||
*
|
||||
* map<string,map<string,string>>
|
||||
*
|
||||
* vector<pair<string,vector<pair<string,string>>>>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
// Exactly one parameter is required
|
||||
if (attributes.size() != 1) {
|
||||
ret.first = make_error_code(error::invalid_parameters);
|
||||
@@ -500,17 +529,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
m_enabled = true;
|
||||
m_enabled = true;*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns true if this object impliments permessage_compress functionality
|
||||
/// Returns true if this object impliments permessage_deflate functionality
|
||||
bool is_implimented() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// returns true if this object is initialized and ready to provide
|
||||
/// permessage_compress functionality.
|
||||
/// permessage_deflate functionality.
|
||||
bool is_enabled() const {
|
||||
return m_enabled;
|
||||
}
|
||||
@@ -542,8 +571,8 @@ private:
|
||||
method_ptr m_method;
|
||||
};
|
||||
|
||||
} // namespace permessage_compress
|
||||
} // namespace permessage_deflate
|
||||
} // namespace extensions
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGECOMPRESS_HPP
|
||||
#endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
|
||||
@@ -198,7 +198,7 @@ struct extended_header {
|
||||
copy_payload(payload_size);
|
||||
}
|
||||
|
||||
extended_header(uint64_t payload_size, int32_t masking_key) {
|
||||
extended_header(uint64_t payload_size, uint32_t masking_key) {
|
||||
std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
|
||||
|
||||
// Copy payload size
|
||||
@@ -590,8 +590,13 @@ inline size_t prepare_masking_key(const masking_key_type& key) {
|
||||
* to zero and less than sizeof(size_t).
|
||||
*/
|
||||
inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) {
|
||||
size_t temp = prepared_key << (sizeof(size_t)-offset)*8;
|
||||
return (prepared_key >> offset*8) | temp;
|
||||
if (lib::net::is_little_endian()) {
|
||||
size_t temp = prepared_key << (sizeof(size_t)-offset)*8;
|
||||
return (prepared_key >> offset*8) | temp;
|
||||
} else {
|
||||
size_t temp = prepared_key >> (sizeof(size_t)-offset)*8;
|
||||
return (prepared_key << offset*8) | temp;
|
||||
}
|
||||
}
|
||||
|
||||
/// Byte by byte mask/unmask
|
||||
@@ -735,13 +740,13 @@ inline void word_mask_exact(uint8_t* data, size_t length, const
|
||||
*
|
||||
* @return the prepared_key shifted to account for the input length
|
||||
*/
|
||||
inline size_t word_mask_circ(uint8_t* input, uint8_t* output, size_t length,
|
||||
inline size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length,
|
||||
size_t prepared_key)
|
||||
{
|
||||
size_t n = length / sizeof(size_t); // whole words
|
||||
size_t l = length - (n * sizeof(size_t)); // remaining bytes
|
||||
size_t* input_word = reinterpret_cast<size_t*>(input);
|
||||
size_t* output_word = reinterpret_cast<size_t*>(output);
|
||||
size_t * input_word = reinterpret_cast<size_t *>(input);
|
||||
size_t * output_word = reinterpret_cast<size_t *>(output);
|
||||
|
||||
// mask word by word
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -749,10 +754,11 @@ inline size_t word_mask_circ(uint8_t* input, uint8_t* output, size_t length,
|
||||
}
|
||||
|
||||
// mask partial word at the end
|
||||
if (l > 0) {
|
||||
size_t r = 8*(sizeof(size_t) - l); // convert from bytes to bits
|
||||
output_word[n] = input_word[n] ^ ((prepared_key << r) >> r);
|
||||
}
|
||||
size_t start = length - l;
|
||||
uint8_t * byte_key = reinterpret_cast<uint8_t *>(&prepared_key);
|
||||
for (size_t i = 0; i < l; ++i) {
|
||||
output[start+i] = input[start+i] ^ byte_key[i];
|
||||
}
|
||||
|
||||
return circshift_prepared_key(prepared_key,l);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2013, 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:
|
||||
@@ -39,6 +39,9 @@ namespace http {
|
||||
// Maximum size in bytes before rejecting an HTTP header as too big.
|
||||
const size_t max_header_size = 16000;
|
||||
|
||||
// Number of bytes to use for temporary istream read buffers
|
||||
const size_t istream_buffer = 512;
|
||||
|
||||
// invalid HTTP token characters
|
||||
// 0x00 - 0x32, 0x7f-0xff
|
||||
// ( ) < > @ , ; : \ " / [ ] ? = { }
|
||||
|
||||
@@ -131,6 +131,45 @@ inline size_t response::consume(const char *buf, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t response::consume(std::istream & s) {
|
||||
char buf[istream_buffer];
|
||||
size_t bytes_read;
|
||||
size_t bytes_processed;
|
||||
size_t total = 0;
|
||||
|
||||
while (s.good()) {
|
||||
s.getline(buf,istream_buffer);
|
||||
bytes_read = static_cast<size_t>(s.gcount());
|
||||
|
||||
if (s.fail() || s.eof()) {
|
||||
bytes_processed = this->consume(buf,bytes_read);
|
||||
total += bytes_processed;
|
||||
|
||||
if (bytes_processed != bytes_read) {
|
||||
// problem
|
||||
break;
|
||||
}
|
||||
} else if (s.bad()) {
|
||||
// problem
|
||||
break;
|
||||
} else {
|
||||
// the delimiting newline was found. Replace the trailing null with
|
||||
// the newline that was discarded, since our raw consume function
|
||||
// expects the newline to be be there.
|
||||
buf[bytes_read-1] = '\n';
|
||||
bytes_processed = this->consume(buf,bytes_read);
|
||||
total += bytes_processed;
|
||||
|
||||
if (bytes_processed != bytes_read) {
|
||||
// problem
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
inline bool response::parse_complete(std::istream& s) {
|
||||
// parse a complete header (ie \r\n\r\n MUST be in the input stream)
|
||||
std::string response;
|
||||
|
||||
@@ -104,7 +104,9 @@ InputIterator extract_lws(InputIterator begin, InputIterator end) {
|
||||
InputIterator it = begin;
|
||||
|
||||
// strip leading CRLF
|
||||
if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' && is_whitespace_char(*(begin+2))) {
|
||||
if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' &&
|
||||
is_whitespace_char(static_cast<unsigned char>(*(begin+2))))
|
||||
{
|
||||
it+=3;
|
||||
}
|
||||
|
||||
@@ -156,8 +158,12 @@ struct parameter {
|
||||
typedef std::vector<parameter> parameter_list;
|
||||
*/
|
||||
|
||||
//typedef std::map<std::string,std::string> string_map;
|
||||
//typedef std::vector< std::pair< std::string, attribute_list > > parameter_list;
|
||||
|
||||
typedef std::map<std::string,std::string> attribute_list;
|
||||
typedef std::map<std::string,attribute_list> parameter_list;
|
||||
//typedef std::map<std::string,attribute_list> parameter_list;
|
||||
typedef std::vector< std::pair< std::string, attribute_list > > parameter_list;
|
||||
|
||||
template <typename InputIterator>
|
||||
InputIterator extract_attributes(InputIterator begin, InputIterator end,
|
||||
@@ -286,7 +292,8 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end,
|
||||
// Safe break point, insert parameter with blank attributes and exit
|
||||
cursor = http::parser::extract_all_lws(cursor,end);
|
||||
if (cursor == end) {
|
||||
parameters[parameter_name] = attributes;
|
||||
//parameters[parameter_name] = attributes;
|
||||
parameters.push_back(std::make_pair(parameter_name,attributes));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -306,7 +313,8 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end,
|
||||
}
|
||||
|
||||
// insert parameter into output list
|
||||
parameters[parameter_name] = attributes;
|
||||
//parameters[parameter_name] = attributes;
|
||||
parameters.push_back(std::make_pair(parameter_name,attributes));
|
||||
|
||||
cursor = http::parser::extract_all_lws(cursor,end);
|
||||
if (cursor == end) {break;}
|
||||
|
||||
@@ -47,6 +47,9 @@ namespace parser {
|
||||
*/
|
||||
class request : public parser {
|
||||
public:
|
||||
typedef request type;
|
||||
typedef lib::shared_ptr<type> ptr;
|
||||
|
||||
typedef parser::attribute_list attribute_list;
|
||||
typedef parser::parameter_list parameter_list;
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace parser {
|
||||
*/
|
||||
class response : public parser {
|
||||
public:
|
||||
typedef response type;
|
||||
typedef lib::shared_ptr<type> ptr;
|
||||
|
||||
response()
|
||||
: m_read(0)
|
||||
, m_buf(new std::string())
|
||||
@@ -81,6 +84,8 @@ public:
|
||||
*/
|
||||
size_t consume(const char *buf, size_t len);
|
||||
|
||||
size_t consume(std::istream & s);
|
||||
|
||||
/// Returns true if the response is ready.
|
||||
/**
|
||||
* @note will never return true if the content length header is not present
|
||||
|
||||
@@ -65,6 +65,11 @@ size_t connection<config>::get_buffered_amount() const {
|
||||
return m_send_buffer_size;
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
session::state::value connection<config>::get_state() const {
|
||||
//scoped_lock_type lock(m_connection_state_lock);
|
||||
return m_state;
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
lib::error_code connection<config>::send(const std::string& payload,
|
||||
@@ -92,7 +97,7 @@ lib::error_code connection<config>::send(typename config::message_type::ptr msg)
|
||||
m_alog.write(log::alevel::devel,"connection send");
|
||||
// TODO:
|
||||
|
||||
if (m_state != session::state::OPEN) {
|
||||
if (m_state != session::state::open) {
|
||||
return error::make_error_code(error::invalid_state);
|
||||
}
|
||||
|
||||
@@ -137,7 +142,7 @@ template <typename config>
|
||||
void connection<config>::ping(const std::string& payload) {
|
||||
m_alog.write(log::alevel::devel,"connection ping");
|
||||
|
||||
if (m_state != session::state::OPEN) {
|
||||
if (m_state != session::state::open) {
|
||||
throw error::make_error_code(error::invalid_state);
|
||||
}
|
||||
|
||||
@@ -170,7 +175,7 @@ template <typename config>
|
||||
void connection<config>::pong(const std::string& payload, lib::error_code& ec) {
|
||||
m_alog.write(log::alevel::devel,"connection pong");
|
||||
|
||||
if (m_state != session::state::OPEN) {
|
||||
if (m_state != session::state::open) {
|
||||
ec = error::make_error_code(error::invalid_state);
|
||||
return;
|
||||
}
|
||||
@@ -218,7 +223,7 @@ void connection<config>::close(const close::status::value code,
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"connection close");
|
||||
|
||||
if (m_state != session::state::OPEN) {
|
||||
if (m_state != session::state::open) {
|
||||
ec = error::make_error_code(error::invalid_state);
|
||||
return;
|
||||
}
|
||||
@@ -316,11 +321,96 @@ void connection<config>::set_uri(uri_ptr uri) {
|
||||
|
||||
|
||||
|
||||
template <typename config>
|
||||
const std::string & connection<config>::get_subprotocol() const {
|
||||
return m_subprotocol;
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
const std::vector<std::string> &
|
||||
connection<config>::get_requested_subprotocols() const {
|
||||
return m_requested_subprotocols;
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::add_subprotocol(const std::string & value,
|
||||
lib::error_code & ec)
|
||||
{
|
||||
if (m_is_server) {
|
||||
ec = error::make_error_code(error::client_only);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the value is empty or has a non-RFC2616 token character it is invalid.
|
||||
if (value.empty() || std::find_if(value.begin(),value.end(),
|
||||
http::is_not_token_char) != value.end())
|
||||
{
|
||||
ec = error::make_error_code(error::invalid_subprotocol);
|
||||
return;
|
||||
}
|
||||
|
||||
m_requested_subprotocols.push_back(value);
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::add_subprotocol(const std::string & value) {
|
||||
lib::error_code ec;
|
||||
this->add_subprotocol(value,ec);
|
||||
if (ec) {
|
||||
throw ec;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::select_subprotocol(const std::string & value,
|
||||
lib::error_code & ec)
|
||||
{
|
||||
if (!m_is_server) {
|
||||
ec = error::make_error_code(error::server_only);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.empty()) {
|
||||
ec = lib::error_code();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_requested_subprotocols.begin(),
|
||||
m_requested_subprotocols.end(),
|
||||
value);
|
||||
|
||||
if (it == m_requested_subprotocols.end()) {
|
||||
ec = error::make_error_code(error::unrequested_subprotocol);
|
||||
return;
|
||||
}
|
||||
|
||||
m_subprotocol = value;
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::select_subprotocol(const std::string & value) {
|
||||
lib::error_code ec;
|
||||
this->select_subprotocol(value,ec);
|
||||
if (ec) {
|
||||
throw ec;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename config>
|
||||
const std::string &
|
||||
connection<config>::get_request_header(const std::string &key) {
|
||||
return m_request.get_header(key);
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
const std::string &
|
||||
connection<config>::get_response_header(const std::string &key) {
|
||||
return m_response.get_header(key);
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::set_status(
|
||||
@@ -459,7 +549,7 @@ void connection<config>::handle_transport_init(const lib::error_code& ec) {
|
||||
|
||||
if (ec) {
|
||||
std::stringstream s;
|
||||
s << "handle_transport_init recieved error: "<< ec;
|
||||
s << "handle_transport_init recieved error: "<< ec.message();
|
||||
m_elog.write(log::elevel::fatal,s.str());
|
||||
|
||||
this->terminate();
|
||||
@@ -516,7 +606,7 @@ void connection<config>::handle_handshake_read(const lib::error_code& ec,
|
||||
|
||||
if (ec) {
|
||||
std::stringstream s;
|
||||
s << "error in handle_read_handshake: "<< ec;
|
||||
s << "error in handle_read_handshake: "<< ec.message();
|
||||
m_elog.write(log::elevel::fatal,s.str());
|
||||
this->terminate();
|
||||
return;
|
||||
@@ -579,6 +669,14 @@ void connection<config>::handle_handshake_read(const lib::error_code& ec,
|
||||
}
|
||||
}
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,m_request.raw());
|
||||
if (m_request.get_header("Sec-WebSocket-Key3") != "") {
|
||||
m_alog.write(log::alevel::devel,
|
||||
utility::to_hex(m_request.get_header("Sec-WebSocket-Key3")));
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining bytes in m_buf are frame data. Copy them to the
|
||||
// beginning of the buffer and note the length. They will be read after
|
||||
// the handshake completes and before more bytes are read.
|
||||
@@ -631,7 +729,7 @@ template <typename config>
|
||||
void connection<config>::handle_read_frame(const lib::error_code& ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"connection handle_read_frame");
|
||||
//m_alog.write(log::alevel::devel,"connection handle_read_frame");
|
||||
|
||||
this->atomic_state_check(
|
||||
istate::PROCESS_CONNECTION,
|
||||
@@ -639,8 +737,16 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
|
||||
);
|
||||
|
||||
if (ec) {
|
||||
if (ec == transport::error::eof) {
|
||||
// we expect to get eof if the connection is closed already
|
||||
if (m_state == session::state::closed) {
|
||||
m_alog.write(log::alevel::devel,"got eof from closed con");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream s;
|
||||
s << "error in handle_read_frame: " << ec;
|
||||
s << "error in handle_read_frame: " << ec.message() << " (" << ec << ")";
|
||||
m_elog.write(log::elevel::fatal,s.str());
|
||||
this->terminate();
|
||||
return;
|
||||
@@ -655,14 +761,18 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
|
||||
|
||||
size_t p = 0;
|
||||
|
||||
std::stringstream s;
|
||||
s << "p = " << p << " bytes transferred = " << bytes_transferred;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "p = " << p << " bytes transferred = " << bytes_transferred;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
while (p < bytes_transferred) {
|
||||
s.str("");
|
||||
s << "calling consume with " << bytes_transferred-p << " bytes";
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "calling consume with " << bytes_transferred-p << " bytes";
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
lib::error_code ec;
|
||||
|
||||
@@ -671,11 +781,12 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
|
||||
bytes_transferred-p,
|
||||
ec
|
||||
);
|
||||
|
||||
s.str("");
|
||||
s << "bytes left after consume: " << bytes_transferred-p;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "bytes left after consume: " << bytes_transferred-p;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
}
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::rerror,"consume error: "+ec.message());
|
||||
|
||||
@@ -698,7 +809,7 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
|
||||
}
|
||||
|
||||
if (m_processor->ready()) {
|
||||
m_alog.write(log::alevel::devel,"consume ended in ready");
|
||||
//m_alog.write(log::alevel::devel,"consume ended in ready");
|
||||
|
||||
message_ptr msg = m_processor->get_message();
|
||||
|
||||
@@ -838,13 +949,21 @@ bool connection<config>::process_handshake_request() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract subprotocols
|
||||
lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
|
||||
m_requested_subprotocols);
|
||||
|
||||
if (subp_ec) {
|
||||
// should we do anything?
|
||||
}
|
||||
|
||||
// Ask application to validate the connection
|
||||
if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
|
||||
m_response.set_status(http::status_code::switching_protocols);
|
||||
|
||||
// Write the appropriate response headers based on request and
|
||||
// processor version
|
||||
ec = m_processor->process_handshake(m_request,m_response);
|
||||
ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
|
||||
|
||||
if (ec) {
|
||||
std::stringstream s;
|
||||
@@ -938,7 +1057,9 @@ void connection<config>::send_http_response() {
|
||||
|
||||
// Set some common headers
|
||||
m_response.replace_header("Server",m_user_agent);
|
||||
|
||||
|
||||
|
||||
|
||||
// have the processor generate the raw bytes for the wire (if it exists)
|
||||
if (m_processor) {
|
||||
m_handshake_buffer = m_processor->get_raw(m_response);
|
||||
@@ -949,6 +1070,10 @@ void connection<config>::send_http_response() {
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
|
||||
if (m_response.get_header("Sec-WebSocket-Key3") != "") {
|
||||
m_alog.write(log::alevel::devel,
|
||||
utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
|
||||
}
|
||||
}
|
||||
|
||||
// write raw bytes
|
||||
@@ -1004,8 +1129,8 @@ void connection<config>::handle_send_http_response(
|
||||
this->atomic_state_change(
|
||||
istate::PROCESS_HTTP_REQUEST,
|
||||
istate::PROCESS_CONNECTION,
|
||||
session::state::CONNECTING,
|
||||
session::state::OPEN,
|
||||
session::state::connecting,
|
||||
session::state::open,
|
||||
"handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
|
||||
);
|
||||
|
||||
@@ -1020,14 +1145,14 @@ template <typename config>
|
||||
void connection<config>::send_http_request() {
|
||||
m_alog.write(log::alevel::devel,"connection send_http_request");
|
||||
|
||||
// TODO: origin header
|
||||
// TODO: subprotocol requests
|
||||
// TODO: origin header?
|
||||
|
||||
// Have the protocol processor fill in the appropriate fields based on the
|
||||
// selected client version
|
||||
if (m_processor) {
|
||||
lib::error_code ec;
|
||||
ec = m_processor->client_handshake_request(m_request,m_uri);
|
||||
ec = m_processor->client_handshake_request(m_request,m_uri,
|
||||
m_requested_subprotocols);
|
||||
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::fatal,
|
||||
@@ -1044,7 +1169,7 @@ void connection<config>::send_http_request() {
|
||||
if (m_request.get_header("User-Agent") == "") {
|
||||
m_request.replace_header("User-Agent",m_user_agent);
|
||||
}
|
||||
|
||||
|
||||
m_handshake_buffer = m_request.raw();
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
@@ -1127,7 +1252,7 @@ void connection<config>::handle_read_http_response(const lib::error_code& ec,
|
||||
return;
|
||||
}
|
||||
|
||||
m_elog.write(log::elevel::rerror,std::string("Raw response: ")+m_response.raw());
|
||||
m_alog.write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
|
||||
|
||||
if (m_response.headers_ready()) {
|
||||
lib::error_code ec = m_processor->validate_server_handshake_response(
|
||||
@@ -1147,8 +1272,8 @@ void connection<config>::handle_read_http_response(const lib::error_code& ec,
|
||||
this->atomic_state_change(
|
||||
istate::READ_HTTP_RESPONSE,
|
||||
istate::PROCESS_CONNECTION,
|
||||
session::state::CONNECTING,
|
||||
session::state::OPEN,
|
||||
session::state::connecting,
|
||||
session::state::open,
|
||||
"handle_read_http_response must be called from READ_HTTP_RESPONSE state"
|
||||
);
|
||||
|
||||
@@ -1189,19 +1314,22 @@ void connection<config>::terminate() {
|
||||
|
||||
transport_con_type::shutdown();
|
||||
|
||||
if (m_state == session::state::CONNECTING) {
|
||||
m_state = session::state::CLOSED;
|
||||
if (m_state == session::state::connecting) {
|
||||
m_state = session::state::closed;
|
||||
if (m_fail_handler) {
|
||||
m_fail_handler(m_connection_hdl);
|
||||
}
|
||||
} else if (m_state != session::state::CLOSED) {
|
||||
m_state = session::state::CLOSED;
|
||||
} else if (m_state != session::state::closed) {
|
||||
m_state = session::state::closed;
|
||||
if (m_close_handler) {
|
||||
m_close_handler(m_connection_hdl);
|
||||
}
|
||||
} else {
|
||||
m_alog.write(log::alevel::devel,"terminate called on connection that was already terminated");
|
||||
return;
|
||||
}
|
||||
|
||||
log_close_result();
|
||||
} catch (const std::exception& e) {
|
||||
m_elog.write(log::elevel::warn,
|
||||
std::string("terminate failed. Reason was: ") + e.what());
|
||||
@@ -1222,9 +1350,8 @@ void connection<config>::terminate() {
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::write_frame() {
|
||||
m_alog.write(log::alevel::devel,"connection write_frame");
|
||||
//m_alog.write(log::alevel::devel,"connection write_frame");
|
||||
|
||||
message_ptr msg;
|
||||
{
|
||||
scoped_lock_type lock(m_write_lock);
|
||||
|
||||
@@ -1238,9 +1365,9 @@ void connection<config>::write_frame() {
|
||||
|
||||
// Get the next message in the queue. This will return an empty
|
||||
// message if the queue was empty.
|
||||
msg = write_pop();
|
||||
m_current_msg = write_pop();
|
||||
|
||||
if (!msg) {
|
||||
if (!m_current_msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1250,13 +1377,15 @@ void connection<config>::write_frame() {
|
||||
m_write_flag = true;
|
||||
}
|
||||
|
||||
const std::string& header = msg->get_header();
|
||||
const std::string& payload = msg->get_payload();
|
||||
const std::string& header = m_current_msg->get_header();
|
||||
const std::string& payload = m_current_msg->get_payload();
|
||||
|
||||
m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
|
||||
m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
|
||||
|
||||
|
||||
|
||||
if (m_alog.static_test(log::alevel::frame_header)) {
|
||||
if (m_alog.dynamic_test(log::alevel::frame_header)) {
|
||||
std::stringstream s;
|
||||
s << "Dispatching write with " << header.size()
|
||||
<< " header bytes and " << payload.size()
|
||||
@@ -1264,16 +1393,19 @@ void connection<config>::write_frame() {
|
||||
m_alog.write(log::alevel::frame_header,s.str());
|
||||
m_alog.write(log::alevel::frame_header,"Header: "+utility::to_hex(header));
|
||||
}
|
||||
}
|
||||
if (m_alog.static_test(log::alevel::frame_payload)) {
|
||||
if (m_alog.dynamic_test(log::alevel::frame_payload)) {
|
||||
m_alog.write(log::alevel::frame_payload,"Payload: "+utility::to_hex(payload));
|
||||
}
|
||||
}
|
||||
|
||||
transport_con_type::async_write(
|
||||
m_send_buffer,
|
||||
lib::bind(
|
||||
&type::handle_write_frame,
|
||||
type::shared_from_this(),
|
||||
msg->get_terminal(),
|
||||
m_current_msg->get_terminal(),
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
@@ -1284,6 +1416,7 @@ void connection<config>::handle_write_frame(bool terminate,
|
||||
const lib::error_code& ec)
|
||||
{
|
||||
m_send_buffer.clear();
|
||||
m_current_msg.reset();
|
||||
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::fatal,"error in handle_write_frame: "+ec.message());
|
||||
@@ -1441,7 +1574,7 @@ void connection<config>::process_control_frame(typename
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state == session::state::OPEN) {
|
||||
if (m_state == session::state::open) {
|
||||
std::stringstream s;
|
||||
s << "Received close frame with code " << m_remote_close_code
|
||||
<< " and reason " << m_remote_close_reason;
|
||||
@@ -1452,7 +1585,7 @@ void connection<config>::process_control_frame(typename
|
||||
m_elog.write(log::elevel::devel,
|
||||
"send_close_ack error: "+ec.message());
|
||||
}
|
||||
} else if (m_state == session::state::CLOSING) {
|
||||
} else if (m_state == session::state::closing) {
|
||||
// ack of our close
|
||||
m_alog.write(log::alevel::devel,"Got acknowledgement of close");
|
||||
this->terminate();
|
||||
@@ -1650,7 +1783,7 @@ void connection<config>::log_open_result()
|
||||
s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
|
||||
|
||||
// Remote endpoint address
|
||||
s << "Unknown" << " ";
|
||||
s << transport_con_type::get_remote_endpoint() << " ";
|
||||
|
||||
// Version string if WebSocket
|
||||
if (version != -1) {
|
||||
@@ -1659,8 +1792,13 @@ void connection<config>::log_open_result()
|
||||
|
||||
// User Agent
|
||||
std::string ua = m_request.get_header("User-Agent");
|
||||
s << (ua == "" ? "NULL" : ua) << " ";
|
||||
|
||||
if (ua == "") {
|
||||
s << "\"\" ";
|
||||
} else {
|
||||
// check if there are any quotes in the user agent
|
||||
s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
|
||||
}
|
||||
|
||||
// URI
|
||||
s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
|
||||
|
||||
@@ -1670,6 +1808,20 @@ void connection<config>::log_open_result()
|
||||
m_alog.write(log::alevel::connect,s.str());
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::log_close_result()
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
s << "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) << "]";
|
||||
|
||||
m_alog.write(log::alevel::disconnect,s.str());
|
||||
}
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
|
||||
|
||||
@@ -174,7 +174,7 @@ void endpoint<connection,config>::close(connection_hdl hdl,
|
||||
const close::status::value code, const std::string & reason)
|
||||
{
|
||||
lib::error_code ec;
|
||||
send(hdl,code,reason,ec);
|
||||
close(hdl,code,reason,ec);
|
||||
if (ec) { throw ec; }
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,17 @@ inline std::string to_hex(const char* input,size_t length) {
|
||||
return to_hex(reinterpret_cast<const uint8_t*>(input),length);
|
||||
}
|
||||
|
||||
inline std::string string_replace_all(std::string subject, const std::string&
|
||||
search, const std::string& replace)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while((pos = subject.find(search, pos)) != std::string::npos) {
|
||||
subject.replace(pos, search.length(), replace);
|
||||
pos += replace.length();
|
||||
}
|
||||
return subject;
|
||||
}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace websocketpp
|
||||
|
||||
|
||||
@@ -62,7 +62,16 @@ public:
|
||||
, m_dynamic_channels(0)
|
||||
, m_out(out) {}
|
||||
|
||||
void set_ostream(std::ostream* out) {
|
||||
m_out = out;
|
||||
}
|
||||
|
||||
void set_channels(level channels) {
|
||||
if (channels == names::none) {
|
||||
clear_channels(names::all);
|
||||
return;
|
||||
}
|
||||
|
||||
scoped_lock_type lock(m_lock);
|
||||
m_dynamic_channels |= (channels & m_static_channels);
|
||||
}
|
||||
@@ -78,6 +87,7 @@ public:
|
||||
*m_out << "[" << get_timestamp() << "] "
|
||||
<< "[" << names::channel_name(channel) << "] "
|
||||
<< msg << "\n";
|
||||
m_out->flush();
|
||||
}
|
||||
|
||||
void write(level channel, const char* msg) {
|
||||
@@ -86,29 +96,29 @@ public:
|
||||
*m_out << "[" << get_timestamp() << "] "
|
||||
<< "[" << names::channel_name(channel) << "] "
|
||||
<< msg << "\n";
|
||||
m_out->flush();
|
||||
}
|
||||
|
||||
bool static_test(level channel) const {
|
||||
return ((channel & m_static_channels) != 0);
|
||||
}
|
||||
|
||||
bool dynamic_test(level channel) {
|
||||
return ((channel & m_dynamic_channels) != 0);
|
||||
}
|
||||
private:
|
||||
typedef typename concurrency::scoped_lock_type scoped_lock_type;
|
||||
typedef typename concurrency::mutex_type mutex_type;
|
||||
|
||||
bool dynamic_test(level channel) {
|
||||
return ((channel & m_dynamic_channels) != 0);
|
||||
}
|
||||
|
||||
const char* get_timestamp() {
|
||||
std::time_t t = std::time(NULL);
|
||||
std::strftime(buffer,30,"%Y-%m-%d %H:%M:%S%z",std::localtime(&t));
|
||||
std::strftime(buffer,39,"%Y-%m-%d %H:%M:%S%z",std::localtime(&t));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
mutex_type m_lock;
|
||||
|
||||
char buffer[30];
|
||||
|
||||
char buffer[40];
|
||||
const level m_static_channels;
|
||||
level m_dynamic_channels;
|
||||
std::ostream* m_out;
|
||||
|
||||
@@ -202,7 +202,7 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
std::memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
@@ -374,9 +374,9 @@ md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : static_cast<int>(nbytes));
|
||||
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
std::memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
@@ -390,7 +390,7 @@ md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
memcpy(pms->buf, p, left);
|
||||
std::memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -154,6 +154,9 @@ enum processor_errors {
|
||||
/// Using a reason requires a close code
|
||||
reason_requires_code,
|
||||
|
||||
/// Error parsing subprotocols
|
||||
subprotocol_parse_error,
|
||||
|
||||
/// Error parsing extensions
|
||||
extension_parse_error,
|
||||
|
||||
@@ -223,6 +226,8 @@ public:
|
||||
return "Invalid close code used";
|
||||
case error::reason_requires_code:
|
||||
return "Using a close reason requires a valid close code";
|
||||
case error::subprotocol_parse_error:
|
||||
return "Error parsing subprotocol header";
|
||||
case error::extension_parse_error:
|
||||
return "Error parsing extension header";
|
||||
case error::extensions_disabled:
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
|
||||
#include <websocketpp/processors/processor.hpp>
|
||||
|
||||
#include <websocketpp/frame.hpp>
|
||||
#include <websocketpp/utf8_validator.hpp>
|
||||
|
||||
#include <websocketpp/md5/md5.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
@@ -62,7 +65,11 @@ public:
|
||||
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
|
||||
|
||||
explicit hybi00(bool secure, bool server, msg_manager_ptr manager)
|
||||
: processor<config>(secure, server) {}
|
||||
: processor<config>(secure, server)
|
||||
, msg_hdr(0x00)
|
||||
, msg_ftr(0xff)
|
||||
, m_state(HEADER)
|
||||
, m_msg_manager(manager) {}
|
||||
|
||||
int get_version() const {
|
||||
return 0;
|
||||
@@ -91,8 +98,8 @@ public:
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
lib::error_code process_handshake(const request_type& req,
|
||||
response_type& res) const
|
||||
lib::error_code process_handshake(const request_type& req, const
|
||||
std::string & subprotocol, response_type& res) const
|
||||
{
|
||||
char key_final[16];
|
||||
|
||||
@@ -117,7 +124,7 @@ public:
|
||||
md5::md5_hash_string(std::string(key_final,16))
|
||||
);
|
||||
|
||||
res.append_header("Upgrade","websocket");
|
||||
res.append_header("Upgrade","WebSocket");
|
||||
res.append_header("Connection","Upgrade");
|
||||
|
||||
// Echo back client's origin unless our local application set a
|
||||
@@ -133,12 +140,16 @@ public:
|
||||
res.append_header("Sec-WebSocket-Location",uri->str());
|
||||
}
|
||||
|
||||
if (subprotocol != "") {
|
||||
res.replace_header("Sec-WebSocket-Protocol",subprotocol);
|
||||
}
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
// outgoing client connection processing is not supported for this version
|
||||
lib::error_code client_handshake_request(request_type& req, uri_ptr uri)
|
||||
const
|
||||
lib::error_code client_handshake_request(request_type& req, uri_ptr uri,
|
||||
const std::vector<std::string> & subprotocols) const
|
||||
{
|
||||
return error::make_error_code(error::no_protocol_support);
|
||||
}
|
||||
@@ -150,13 +161,22 @@ public:
|
||||
}
|
||||
|
||||
std::string get_raw(const response_type& res) const {
|
||||
return res.raw() + res.get_header("Sec-WebSocket-Key3");
|
||||
response_type temp = res;
|
||||
temp.remove_header("Sec-WebSocket-Key3");
|
||||
return temp.raw() + res.get_header("Sec-WebSocket-Key3");
|
||||
}
|
||||
|
||||
const std::string& get_origin(const request_type& r) const {
|
||||
return r.get_header("Origin");
|
||||
}
|
||||
|
||||
// hybi00 doesn't support subprotocols so there never will be any requested
|
||||
lib::error_code extract_subprotocols(const request_type & req,
|
||||
std::vector<std::string> & subprotocol_list)
|
||||
{
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
uri_ptr get_uri(const request_type& request) const {
|
||||
std::string h = request.get_header("Host");
|
||||
|
||||
@@ -188,12 +208,64 @@ public:
|
||||
|
||||
/// Process new websocket connection bytes
|
||||
size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) {
|
||||
ec = make_error_code(error::not_implimented);
|
||||
return 0;
|
||||
// if in state header we are expecting a 0x00 byte, if we don't get one
|
||||
// it is a fatal error
|
||||
size_t p = 0; // bytes processed
|
||||
size_t l = 0;
|
||||
|
||||
ec = lib::error_code();
|
||||
|
||||
while (p < len) {
|
||||
if (m_state == HEADER) {
|
||||
if (buf[p] == msg_hdr) {
|
||||
p++;
|
||||
m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1);
|
||||
|
||||
if (!m_msg_ptr) {
|
||||
ec = make_error_code(websocketpp::error::no_incoming_buffers);
|
||||
m_state = FATAL_ERROR;
|
||||
} else {
|
||||
m_state = PAYLOAD;
|
||||
}
|
||||
} else {
|
||||
ec = make_error_code(error::protocol_violation);
|
||||
m_state = FATAL_ERROR;
|
||||
}
|
||||
} else if (m_state == PAYLOAD) {
|
||||
uint8_t *it = std::find(buf+p,buf+len,msg_ftr);
|
||||
|
||||
// 0 1 2 3 4 5
|
||||
// 0x00 0x23 0x23 0x23 0xff 0xXX
|
||||
|
||||
// Copy payload bytes into message
|
||||
l = static_cast<size_t>(it-(buf+p));
|
||||
m_msg_ptr->append_payload(buf+p,l);
|
||||
p += l;
|
||||
|
||||
if (it != buf+len) {
|
||||
// message is done, copy it and the trailing
|
||||
p++;
|
||||
// TODO: validation
|
||||
m_state = READY;
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we get one, we create a new message and move to application state
|
||||
|
||||
// if in state application we are copying bytes into the output message
|
||||
// and validating them for UTF8 until we hit a 0xff byte. Once we hit
|
||||
// 0x00, the message is complete and is dispatched. Then we go back to
|
||||
// header state.
|
||||
|
||||
//ec = make_error_code(error::not_implimented);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool ready() const {
|
||||
return false;
|
||||
return (m_state == READY);
|
||||
}
|
||||
|
||||
bool get_error() const {
|
||||
@@ -201,7 +273,10 @@ public:
|
||||
}
|
||||
|
||||
message_ptr get_message() {
|
||||
return message_ptr();
|
||||
message_ptr ret = m_msg_ptr;
|
||||
m_msg_ptr = message_ptr();
|
||||
m_state = HEADER;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Prepare a message for writing
|
||||
@@ -211,19 +286,37 @@ public:
|
||||
*/
|
||||
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
|
||||
{
|
||||
// assert msg
|
||||
if (!in || !out) {
|
||||
return make_error_code(error::invalid_arguments);
|
||||
}
|
||||
|
||||
// check if the message is prepared already
|
||||
// TODO: check if the message is prepared already
|
||||
|
||||
// validate opcode
|
||||
if (in->get_opcode() != frame::opcode::text) {
|
||||
return make_error_code(error::invalid_opcode);
|
||||
}
|
||||
|
||||
std::string& i = in->get_raw_payload();
|
||||
//std::string& o = out->get_raw_payload();
|
||||
|
||||
// validate payload utf8
|
||||
|
||||
// if we are a client generate a masking key
|
||||
|
||||
if (!utf8_validator::validate(i)) {
|
||||
return make_error_code(error::invalid_payload);
|
||||
}
|
||||
|
||||
// generate header
|
||||
// perform compression
|
||||
// perform masking
|
||||
out->set_header(std::string(reinterpret_cast<const char*>(&msg_hdr),1));
|
||||
|
||||
// process payload
|
||||
out->set_payload(i);
|
||||
out->append_payload(std::string(reinterpret_cast<const char*>(&msg_ftr),1));
|
||||
|
||||
// hybi00 doesn't support compression
|
||||
// hybi00 doesn't have masking
|
||||
|
||||
out->set_prepared(true);
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
@@ -240,11 +333,21 @@ public:
|
||||
lib::error_code prepare_close(close::status::value code,
|
||||
const std::string & reason, message_ptr out) const
|
||||
{
|
||||
return lib::error_code(error::no_protocol_support);
|
||||
if (!out) {
|
||||
return lib::error_code(error::invalid_arguments);
|
||||
}
|
||||
|
||||
std::string val;
|
||||
val.append(1,0xff);
|
||||
val.append(1,0x00);
|
||||
out->set_payload(val);
|
||||
out->set_prepared(true);
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
private:
|
||||
void decode_client_key(const std::string& key, char* result) const {
|
||||
int spaces = 0;
|
||||
unsigned int spaces = 0;
|
||||
std::string digits = "";
|
||||
uint32_t num;
|
||||
|
||||
@@ -257,7 +360,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
num = strtoul(digits.c_str(), NULL, 10);
|
||||
num = static_cast<uint32_t>(strtoul(digits.c_str(), NULL, 10));
|
||||
if (spaces > 0 && num > 0) {
|
||||
num = htonl(num/spaces);
|
||||
std::copy(reinterpret_cast<char*>(&num),
|
||||
@@ -267,6 +370,22 @@ private:
|
||||
std::fill(result,result+4,0);
|
||||
}
|
||||
}
|
||||
|
||||
enum state {
|
||||
HEADER = 0,
|
||||
PAYLOAD = 1,
|
||||
READY = 2,
|
||||
FATAL_ERROR = 3
|
||||
};
|
||||
|
||||
const uint8_t msg_hdr;
|
||||
const uint8_t msg_ftr;
|
||||
|
||||
state m_state;
|
||||
|
||||
msg_manager_ptr m_msg_manager;
|
||||
message_ptr m_msg_ptr;
|
||||
utf8_validator::validator m_validator;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace processor {
|
||||
template <typename config>
|
||||
class hybi07 : public hybi08<config> {
|
||||
public:
|
||||
typedef typename config::request_type request_type;
|
||||
|
||||
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
|
||||
typedef typename config::rng_type rng_type;
|
||||
|
||||
@@ -47,6 +49,13 @@ public:
|
||||
rng_type& rng)
|
||||
: hybi08<config>(secure, server, manager, rng) {}
|
||||
|
||||
// outgoing client connection processing is not supported for this version
|
||||
lib::error_code client_handshake_request(request_type& req, uri_ptr uri,
|
||||
const std::vector<std::string> & subprotocols) const
|
||||
{
|
||||
return error::make_error_code(error::no_protocol_support);
|
||||
}
|
||||
|
||||
int get_version() const {
|
||||
return 7;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,13 @@ public:
|
||||
rng_type& rng)
|
||||
: hybi13<config>(secure, server, manager, rng) {}
|
||||
|
||||
// outgoing client connection processing is not supported for this version
|
||||
lib::error_code client_handshake_request(request_type& req, uri_ptr uri,
|
||||
const std::vector<std::string> & subprotocols) const
|
||||
{
|
||||
return error::make_error_code(error::no_protocol_support);
|
||||
}
|
||||
|
||||
int get_version() const {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
typedef typename msg_manager_type::ptr msg_manager_ptr;
|
||||
typedef typename config::rng_type rng_type;
|
||||
|
||||
typedef typename config::permessage_compress_type permessage_compress_type;
|
||||
typedef typename config::permessage_deflate_type permessage_deflate_type;
|
||||
|
||||
typedef std::pair<lib::error_code,std::string> err_str_pair;
|
||||
|
||||
@@ -86,8 +86,8 @@ public:
|
||||
return 13;
|
||||
}
|
||||
|
||||
bool has_permessage_compress() const {
|
||||
return m_permessage_compress.is_implimented();
|
||||
bool has_permessage_deflate() const {
|
||||
return m_permessage_deflate.is_implimented();
|
||||
}
|
||||
|
||||
err_str_pair negotiate_extensions(const request_type& req) {
|
||||
@@ -116,26 +116,28 @@ public:
|
||||
|
||||
typename request_type::parameter_list::const_iterator it;
|
||||
|
||||
// if permessage_compress is implimented, check if it was requested
|
||||
if (m_permessage_compress.is_implimented()) {
|
||||
it = p.find("permessage-compress");
|
||||
|
||||
if (m_permessage_deflate.is_implimented()) {
|
||||
err_str_pair neg_ret;
|
||||
for (it = p.begin(); it != p.end(); ++it) {
|
||||
// look through each extension, if the key is permessage-deflate
|
||||
if (it->first == "permessage-deflate") {
|
||||
std::cout << "mark3: " << std::endl;
|
||||
neg_ret = m_permessage_deflate.negotiate(it->second);
|
||||
|
||||
std::cout << neg_ret.first.message() << " - " << neg_ret.second << std::endl;
|
||||
|
||||
if (it != p.end()) {
|
||||
neg_ret = m_permessage_compress.negotiate(it->second);
|
||||
|
||||
if (neg_ret.first) {
|
||||
// Figure out if this is an error that should halt all
|
||||
// extension negotiations or simply cause negotiation of
|
||||
// this specific extension to fail.
|
||||
std::cout << "permessage-compress negotiation failed: "
|
||||
<< neg_ret.first << " and message "
|
||||
<< neg_ret.second << std::endl;
|
||||
} else {
|
||||
// Note: this list will need commas if WebSocket++ ever
|
||||
// supports more than one extension
|
||||
ret.second += neg_ret.second;
|
||||
if (neg_ret.first) {
|
||||
// Figure out if this is an error that should halt all
|
||||
// extension negotiations or simply cause negotiation of
|
||||
// this specific extension to fail.
|
||||
std::cout << "permessage-compress negotiation failed: "
|
||||
<< neg_ret.first.message() << std::endl;
|
||||
} else {
|
||||
// Note: this list will need commas if WebSocket++ ever
|
||||
// supports more than one extension
|
||||
ret.second += neg_ret.second;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,8 +165,12 @@ public:
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
lib::error_code process_handshake(const request_type& request,
|
||||
response_type& response) const
|
||||
/* TODO: the 'subprotocol' parameter may need to be expanded into a more
|
||||
* generic struct if other user input parameters to the processed handshake
|
||||
* are found.
|
||||
*/
|
||||
lib::error_code process_handshake(const request_type& request, const
|
||||
std::string & subprotocol, response_type& response) const
|
||||
{
|
||||
std::string server_key = request.get_header("Sec-WebSocket-Key");
|
||||
|
||||
@@ -178,11 +184,15 @@ public:
|
||||
response.append_header("Upgrade",constants::upgrade_token);
|
||||
response.append_header("Connection",constants::connection_token);
|
||||
|
||||
if (!subprotocol.empty()) {
|
||||
response.replace_header("Sec-WebSocket-Protocol",subprotocol);
|
||||
}
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
lib::error_code client_handshake_request(request_type& req, uri_ptr
|
||||
uri) const
|
||||
uri, const std::vector<std::string> & subprotocols) const
|
||||
{
|
||||
req.set_method("GET");
|
||||
req.set_uri(uri->get_resource());
|
||||
@@ -193,6 +203,17 @@ public:
|
||||
req.replace_header("Sec-WebSocket-Version","13");
|
||||
req.replace_header("Host",uri->get_host_port());
|
||||
|
||||
if (!subprotocols.empty()) {
|
||||
std::ostringstream result;
|
||||
std::vector<std::string>::const_iterator it = subprotocols.begin();
|
||||
result << *it++;
|
||||
while (it != subprotocols.end()) {
|
||||
result << ", " << *it++;
|
||||
}
|
||||
|
||||
req.replace_header("Sec-WebSocket-Protocol",result.str());
|
||||
}
|
||||
|
||||
// Generate handshake key
|
||||
frame::uint32_converter conv;
|
||||
unsigned char raw_key[16];
|
||||
@@ -250,6 +271,25 @@ public:
|
||||
return r.get_header("Origin");
|
||||
}
|
||||
|
||||
lib::error_code extract_subprotocols(const request_type & req,
|
||||
std::vector<std::string> & subprotocol_list)
|
||||
{
|
||||
if (!req.get_header("Sec-WebSocket-Protocol").empty()) {
|
||||
typename request_type::parameter_list p;
|
||||
|
||||
if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) {
|
||||
typename request_type::parameter_list::const_iterator it;
|
||||
|
||||
for (it = p.begin(); it != p.end(); ++it) {
|
||||
subprotocol_list.push_back(it->first);
|
||||
}
|
||||
} else {
|
||||
return error::make_error_code(error::subprotocol_parse_error);
|
||||
}
|
||||
}
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
uri_ptr get_uri(const request_type& request) const {
|
||||
std::string h = request.get_header("Host");
|
||||
|
||||
@@ -346,7 +386,9 @@ public:
|
||||
// check if this frame is the start of a new message and set up
|
||||
// the appropriate message metadata.
|
||||
frame::opcode::value op = frame::get_opcode(m_basic_header);
|
||||
|
||||
|
||||
// TODO: get_message failure conditions
|
||||
|
||||
if (frame::opcode::is_control(op)) {
|
||||
m_control_msg = msg_metadata(
|
||||
m_msg_manager->get_message(op,m_bytes_needed),
|
||||
@@ -508,7 +550,7 @@ public:
|
||||
|
||||
frame::masking_key_type key;
|
||||
bool masked = !base::m_server;
|
||||
bool compressed = m_permessage_compress.is_enabled()
|
||||
bool compressed = m_permessage_deflate.is_enabled()
|
||||
&& in->get_compressed();
|
||||
bool fin = in->get_fin();
|
||||
|
||||
@@ -529,7 +571,7 @@ public:
|
||||
// prepare payload
|
||||
if (compressed) {
|
||||
// compress and store in o after header.
|
||||
m_permessage_compress.compress(i,o);
|
||||
m_permessage_deflate.compress(i,o);
|
||||
|
||||
// mask in place if necessary
|
||||
if (masked) {
|
||||
@@ -699,11 +741,11 @@ protected:
|
||||
size_t offset = out.size();
|
||||
|
||||
// decompress message if needed.
|
||||
if (m_permessage_compress.is_enabled()
|
||||
if (m_permessage_deflate.is_enabled()
|
||||
&& frame::get_rsv1(m_basic_header))
|
||||
{
|
||||
// Decompress current buffer into the message buffer
|
||||
m_permessage_compress.decompress(buf,len,out);
|
||||
m_permessage_deflate.decompress(buf,len,out);
|
||||
|
||||
// get the length of the newly uncompressed output
|
||||
offset = out.size() - offset;
|
||||
@@ -761,7 +803,7 @@ protected:
|
||||
// a control message.
|
||||
//
|
||||
// TODO: unit tests for this
|
||||
if (frame::get_rsv1(h) && (!m_permessage_compress.is_enabled()
|
||||
if (frame::get_rsv1(h) && (!m_permessage_deflate.is_enabled()
|
||||
|| frame::opcode::is_control(op)))
|
||||
{
|
||||
return make_error_code(error::invalid_rsv_bit);
|
||||
@@ -978,7 +1020,7 @@ protected:
|
||||
state m_state;
|
||||
|
||||
// Extensions
|
||||
permessage_compress_type m_permessage_compress;
|
||||
permessage_deflate_type m_permessage_deflate;
|
||||
};
|
||||
|
||||
} // namespace processor
|
||||
|
||||
@@ -128,9 +128,7 @@ int get_websocket_version(request_type& r) {
|
||||
//
|
||||
// // handle msg;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
||||
template <typename config>
|
||||
class processor {
|
||||
@@ -186,12 +184,14 @@ public:
|
||||
/**
|
||||
* @param req The request to process
|
||||
*
|
||||
* @param subprotocol The subprotocol in use
|
||||
*
|
||||
* @param res The response to store the processed response in
|
||||
*
|
||||
* @return An error code, 0 on success, non-zero for other errors
|
||||
*/
|
||||
virtual lib::error_code process_handshake(const request_type& req,
|
||||
response_type& res) const = 0;
|
||||
virtual lib::error_code process_handshake(const request_type& req, const
|
||||
std::string & subprotocol, response_type& res) const = 0;
|
||||
|
||||
/// Fill in an HTTP request for an outgoing connection handshake
|
||||
/**
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
* @return An error code, 0 on success, non-zero for other errors
|
||||
*/
|
||||
virtual lib::error_code client_handshake_request(request_type& req,
|
||||
uri_ptr uri) const = 0;
|
||||
uri_ptr uri, const std::vector<std::string> & subprotocols) const = 0;
|
||||
|
||||
/// Validate the server's response to an outgoing handshake request
|
||||
/**
|
||||
@@ -220,6 +220,19 @@ public:
|
||||
virtual const std::string& get_origin(const request_type& request)
|
||||
const = 0;
|
||||
|
||||
/// Extracts requested subprotocols from a handshake request
|
||||
/**
|
||||
* Extracts a list of all subprotocols that the client has requested in the
|
||||
* given opening handshake request.
|
||||
*
|
||||
* @param req The request to extract from
|
||||
*
|
||||
* @param subprotocol_list A reference to a vector of strings to store the
|
||||
* results in.
|
||||
*/
|
||||
virtual lib::error_code extract_subprotocols(const request_type & req,
|
||||
std::vector<std::string> & subprotocol_list) = 0;
|
||||
|
||||
/// Extracts client uri from a handshake request
|
||||
virtual uri_ptr get_uri(const request_type& request) const = 0;
|
||||
|
||||
|
||||
@@ -56,7 +56,13 @@ enum value {
|
||||
invalid_num_bytes,
|
||||
|
||||
/// there was an error in the underlying transport library
|
||||
pass_through
|
||||
pass_through,
|
||||
|
||||
/// The connection to the requested proxy server failed
|
||||
proxy_failed,
|
||||
|
||||
/// Invalid Proxy URI
|
||||
proxy_invalid
|
||||
};
|
||||
|
||||
class category : public lib::error_category {
|
||||
@@ -73,6 +79,10 @@ public:
|
||||
return "async_read_at_least call requested more bytes than buffer can store";
|
||||
case error::pass_through:
|
||||
return "Underlying Transport Error";
|
||||
case error::proxy_failed:
|
||||
return "Proxy connection failed";
|
||||
case error::proxy_invalid:
|
||||
return "Invalid proxy URI";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
#include <websocketpp/transport/asio/base.hpp>
|
||||
#include <websocketpp/transport/base/connection.hpp>
|
||||
|
||||
#include <websocketpp/base64/base64.hpp>
|
||||
#include <websocketpp/error.hpp>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
@@ -57,7 +60,7 @@ template <typename config>
|
||||
class connection : public config::socket_type::socket_con_type {
|
||||
public:
|
||||
/// Type of this connection transport component
|
||||
typedef connection<config> type;
|
||||
typedef connection<config> type;
|
||||
/// Type of a shared pointer to this connection transport component
|
||||
typedef lib::shared_ptr<type> ptr;
|
||||
|
||||
@@ -69,139 +72,348 @@ public:
|
||||
typedef typename config::alog_type alog_type;
|
||||
/// Type of this transport's error logging policy
|
||||
typedef typename config::elog_type elog_type;
|
||||
|
||||
|
||||
typedef typename config::request_type request_type;
|
||||
typedef typename request_type::ptr request_ptr;
|
||||
typedef typename config::response_type response_type;
|
||||
typedef typename response_type::ptr response_ptr;
|
||||
|
||||
/// Type of a pointer to the ASIO io_service being used
|
||||
typedef boost::asio::io_service* io_service_ptr;
|
||||
typedef boost::asio::io_service* io_service_ptr;
|
||||
|
||||
// generate and manage our own io_service
|
||||
explicit connection(bool is_server, alog_type& alog, elog_type& elog)
|
||||
: m_is_server(is_server)
|
||||
, m_alog(alog)
|
||||
, m_elog(elog)
|
||||
{
|
||||
// generate and manage our own io_service
|
||||
explicit connection(bool is_server, alog_type& alog, elog_type& elog)
|
||||
: m_is_server(is_server)
|
||||
, m_alog(alog)
|
||||
, m_elog(elog)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"asio con transport constructor");
|
||||
}
|
||||
|
||||
bool is_secure() const {
|
||||
return socket_con_type::is_secure();
|
||||
}
|
||||
|
||||
/// Finish constructing the transport
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*
|
||||
* TODO: this method is not protected because the endpoint needs to call it.
|
||||
* need to figure out if there is a way to friend the endpoint safely across
|
||||
* different compilers.
|
||||
*/
|
||||
}
|
||||
|
||||
bool is_secure() const {
|
||||
return socket_con_type::is_secure();
|
||||
}
|
||||
|
||||
/// Finish constructing the transport
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*
|
||||
* TODO: this method is not protected because the endpoint needs to call it.
|
||||
* need to figure out if there is a way to friend the endpoint safely across
|
||||
* different compilers.
|
||||
*/
|
||||
void init_asio (io_service_ptr io_service) {
|
||||
// do we need to store or use the io_service at this level?
|
||||
m_io_service = io_service;
|
||||
// do we need to store or use the io_service at this level?
|
||||
m_io_service = io_service;
|
||||
|
||||
//m_strand.reset(new boost::asio::strand(*io_service));
|
||||
|
||||
socket_con_type::init_asio(io_service, m_is_server);
|
||||
|
||||
socket_con_type::init_asio(io_service, m_is_server);
|
||||
}
|
||||
|
||||
void set_tcp_init_handler(tcp_init_handler h) {
|
||||
m_tcp_init_handler = h;
|
||||
}
|
||||
|
||||
void set_proxy(const std::string & proxy) {
|
||||
m_proxy = proxy;
|
||||
m_proxy_data.reset(new proxy_data());
|
||||
}
|
||||
void set_proxy_basic_auth(const std::string & u, const std::string & p) {
|
||||
if (m_proxy_data) {
|
||||
std::string val = "Basic "+base64_encode(u + ": " + p);
|
||||
m_proxy_data->req.replace_header("Proxy-Authorization",val);
|
||||
} else {
|
||||
// TODO: should we throw errors with invalid stuff here or just
|
||||
// silently ignore?
|
||||
}
|
||||
}
|
||||
|
||||
const std::string & get_proxy() const {
|
||||
return m_proxy;
|
||||
}
|
||||
|
||||
/// Get the remote endpoint address
|
||||
/**
|
||||
* The iostream transport has no information about the ultimate remote
|
||||
* endpoint. It will return the string "iostream transport". To indicate
|
||||
* this.
|
||||
*
|
||||
* TODO: allow user settable remote endpoint addresses if this seems useful
|
||||
*
|
||||
* @return A string identifying the address of the remote endpoint
|
||||
*/
|
||||
std::string get_remote_endpoint() const {
|
||||
lib::error_code ec;
|
||||
|
||||
std::string ret = socket_con_type::get_remote_endpoint(ec);
|
||||
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::info,ret);
|
||||
return "Unknown";
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the connection handle
|
||||
connection_hdl get_handle() const {
|
||||
return m_connection_hdl;
|
||||
}
|
||||
|
||||
/// initialize the proxy buffers and http parsers
|
||||
/**
|
||||
*
|
||||
* @param authority The address of the server we want the proxy to tunnel to
|
||||
* in the format of a URI authority (host:port)
|
||||
*/
|
||||
lib::error_code proxy_init(const std::string & authority) {
|
||||
if (!m_proxy_data) {
|
||||
return websocketpp::error::make_error_code(websocketpp::error::invalid_state);
|
||||
}
|
||||
m_proxy_data->req.set_version("HTTP/1.1");
|
||||
m_proxy_data->req.set_method("CONNECT");
|
||||
|
||||
m_proxy_data->req.set_uri(authority);
|
||||
m_proxy_data->req.replace_header("Host",authority);
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Initialize transport for reading
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*/
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*/
|
||||
void init(init_handler callback) {
|
||||
m_alog.write(log::alevel::devel,"asio connection init");
|
||||
|
||||
socket_con_type::init(
|
||||
lib::bind(
|
||||
&type::handle_init,
|
||||
this,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_init(init_handler callback, const lib::error_code& ec) {
|
||||
if (m_tcp_init_handler) {
|
||||
m_tcp_init_handler(m_connection_hdl);
|
||||
}
|
||||
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
socket_con_type::init(
|
||||
lib::bind(
|
||||
&type::handle_init,
|
||||
this,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_init(init_handler callback, const lib::error_code& ec) {
|
||||
if (m_tcp_init_handler) {
|
||||
m_tcp_init_handler(m_connection_hdl);
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
// If no error, and we are an insecure connection with a proxy set
|
||||
// issue a proxy connect.
|
||||
if (!m_proxy.empty()) {
|
||||
proxy_write(callback);
|
||||
} else {
|
||||
callback(ec);
|
||||
}
|
||||
}
|
||||
|
||||
void proxy_write(init_handler callback) {
|
||||
if (!m_proxy_data) {
|
||||
m_elog.write(log::elevel::library,
|
||||
"assertion failed: !m_proxy_data in asio::connection::proxy_write");
|
||||
callback(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
|
||||
m_proxy_data->write_buf = m_proxy_data->req.raw();
|
||||
|
||||
m_bufs.push_back(boost::asio::buffer(m_proxy_data->write_buf.data(),
|
||||
m_proxy_data->write_buf.size()));
|
||||
|
||||
m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
|
||||
|
||||
boost::asio::async_write(
|
||||
socket_con_type::get_next_layer(),
|
||||
m_bufs,
|
||||
lib::bind(
|
||||
&type::handle_proxy_write,
|
||||
this,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_proxy_write(init_handler callback, const
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
m_bufs.clear();
|
||||
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::info,
|
||||
"asio handle_proxy_write error: "+ec.message());
|
||||
callback(make_error_code(error::pass_through));
|
||||
} else {
|
||||
proxy_read(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void proxy_read(init_handler callback) {
|
||||
if (!m_proxy_data) {
|
||||
m_elog.write(log::elevel::library,
|
||||
"assertion failed: !m_proxy_data in asio::connection::proxy_read");
|
||||
callback(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
|
||||
boost::asio::async_read_until(
|
||||
socket_con_type::get_socket(),
|
||||
m_proxy_data->read_buf,
|
||||
"\r\n\r\n",
|
||||
lib::bind(
|
||||
&type::handle_proxy_read,
|
||||
this,
|
||||
callback,
|
||||
lib::placeholders::_1,
|
||||
lib::placeholders::_2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_proxy_read(init_handler callback, const
|
||||
boost::system::error_code& ec, size_t bytes_transferred)
|
||||
{
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::info,
|
||||
"asio handle_proxy_read error: "+ec.message());
|
||||
callback(make_error_code(error::pass_through));
|
||||
} else {
|
||||
if (!m_proxy_data) {
|
||||
m_elog.write(log::elevel::library,
|
||||
"assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
|
||||
callback(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
|
||||
std::istream input(&m_proxy_data->read_buf);
|
||||
|
||||
m_proxy_data->res.consume(input);
|
||||
|
||||
if (!m_proxy_data->res.headers_ready()) {
|
||||
// we read until the headers were done in theory but apparently
|
||||
// they aren't. Internal endpoint error.
|
||||
callback(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
|
||||
m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
|
||||
|
||||
if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
|
||||
// got an error response back
|
||||
// TODO: expose this error in a programatically accessible way?
|
||||
// if so, see below for an option on how to do this.
|
||||
std::stringstream s;
|
||||
s << "Proxy connection error: "
|
||||
<< m_proxy_data->res.get_status_code()
|
||||
<< " ("
|
||||
<< m_proxy_data->res.get_status_msg()
|
||||
<< ")";
|
||||
m_elog.write(log::elevel::info,s.str());
|
||||
callback(make_error_code(error::proxy_failed));
|
||||
return;
|
||||
}
|
||||
|
||||
// we have successfully established a connection to the proxy, now
|
||||
// we can continue and the proxy will transparently forward the
|
||||
// WebSocket connection.
|
||||
|
||||
// TODO: decide if we want an on_proxy callback that would allow
|
||||
// access to the proxy response.
|
||||
|
||||
// free the proxy buffers and req/res objects as they aren't needed
|
||||
// anymore
|
||||
m_proxy_data.reset();
|
||||
|
||||
// call the original init handler back.
|
||||
callback(lib::error_code());
|
||||
}
|
||||
}
|
||||
|
||||
/// read at least num_bytes bytes into buf and then call handler.
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
void async_read_at_least(size_t num_bytes, char *buf, size_t len,
|
||||
read_handler handler)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "asio async_read_at_least: " << num_bytes;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
|
||||
if (num_bytes > len) {
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
void async_read_at_least(size_t num_bytes, char *buf, size_t len,
|
||||
read_handler handler)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "asio async_read_at_least: " << num_bytes;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
if (num_bytes > len) {
|
||||
m_elog.write(log::elevel::devel,
|
||||
"asio async_read_at_least error::invalid_num_bytes");
|
||||
handler(make_error_code(transport::error::invalid_num_bytes),
|
||||
size_t(0));
|
||||
return;
|
||||
}
|
||||
|
||||
boost::asio::async_read(
|
||||
socket_con_type::get_socket(),
|
||||
boost::asio::buffer(buf,len),
|
||||
boost::asio::transfer_at_least(num_bytes),
|
||||
lib::bind(
|
||||
&type::handle_async_read,
|
||||
this,
|
||||
handler,
|
||||
lib::placeholders::_1,
|
||||
lib::placeholders::_2
|
||||
)
|
||||
);
|
||||
}
|
||||
handler(make_error_code(transport::error::invalid_num_bytes),
|
||||
size_t(0));
|
||||
return;
|
||||
}
|
||||
|
||||
boost::asio::async_read(
|
||||
socket_con_type::get_socket(),
|
||||
boost::asio::buffer(buf,len),
|
||||
boost::asio::transfer_at_least(num_bytes),
|
||||
lib::bind(
|
||||
&type::handle_async_read,
|
||||
this,
|
||||
handler,
|
||||
lib::placeholders::_1,
|
||||
lib::placeholders::_2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_async_read(read_handler handler, const
|
||||
boost::system::error_code& ec, size_t bytes_transferred)
|
||||
{
|
||||
// TODO: translate this better
|
||||
if (ec) {
|
||||
if (!ec) {
|
||||
handler(lib::error_code(), bytes_transferred);
|
||||
return;
|
||||
}
|
||||
|
||||
// translate boost error codes into more lib::error_codes
|
||||
if (ec == boost::asio::error::eof) {
|
||||
handler(make_error_code(transport::error::eof),
|
||||
bytes_transferred);
|
||||
} else {
|
||||
// other error that we cannot translate into a WebSocket++
|
||||
// transport error. Use pass through and print an info warning
|
||||
// with the original error.
|
||||
std::stringstream s;
|
||||
s << "asio async_read_at_least error::pass_through"
|
||||
<< "Original Error: " << ec << " (" << ec.message() << ")";
|
||||
m_elog.write(log::elevel::devel,s.str());
|
||||
handler(make_error_code(transport::error::pass_through),
|
||||
bytes_transferred);
|
||||
} else {
|
||||
handler(lib::error_code(), bytes_transferred);
|
||||
}
|
||||
<< ", Original Error: " << ec << " (" << ec.message() << ")";
|
||||
m_elog.write(log::elevel::info,s.str());
|
||||
handler(make_error_code(transport::error::pass_through),
|
||||
bytes_transferred);
|
||||
}
|
||||
}
|
||||
|
||||
void async_write(const char* buf, size_t len, write_handler handler) {
|
||||
m_bufs.push_back(boost::asio::buffer(buf,len));
|
||||
|
||||
|
||||
boost::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
lib::bind(
|
||||
&type::handle_async_write,
|
||||
this,
|
||||
handler,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
lib::bind(
|
||||
&type::handle_async_write,
|
||||
this,
|
||||
handler,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void async_write(const std::vector<buffer>& bufs, write_handler handler) {
|
||||
@@ -211,28 +423,28 @@ protected:
|
||||
m_bufs.push_back(boost::asio::buffer((*it).buf,(*it).len));
|
||||
}
|
||||
|
||||
boost::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
lib::bind(
|
||||
&type::handle_async_write,
|
||||
this,
|
||||
handler,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
boost::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
lib::bind(
|
||||
&type::handle_async_write,
|
||||
this,
|
||||
handler,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_async_write(write_handler handler, const
|
||||
boost::system::error_code& ec)
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
m_bufs.clear();
|
||||
// TODO: translate this better
|
||||
if (ec) {
|
||||
handler(make_error_code(error::pass_through));
|
||||
} else {
|
||||
handler(lib::error_code());
|
||||
}
|
||||
// TODO: translate this better
|
||||
if (ec) {
|
||||
handler(make_error_code(error::pass_through));
|
||||
} else {
|
||||
handler(lib::error_code());
|
||||
}
|
||||
}
|
||||
|
||||
/// Set Connection Handle
|
||||
@@ -295,16 +507,22 @@ protected:
|
||||
}
|
||||
}
|
||||
private:
|
||||
// static settings
|
||||
const bool m_is_server;
|
||||
// static settings
|
||||
const bool m_is_server;
|
||||
alog_type& m_alog;
|
||||
elog_type& m_elog;
|
||||
|
||||
// dynamic settings
|
||||
|
||||
// transport state
|
||||
|
||||
// transport resources
|
||||
|
||||
struct proxy_data {
|
||||
request_type req;
|
||||
response_type res;
|
||||
std::string write_buf;
|
||||
boost::asio::streambuf read_buf;
|
||||
};
|
||||
|
||||
std::string m_proxy;
|
||||
lib::shared_ptr<proxy_data> m_proxy_data;
|
||||
|
||||
// transport resources
|
||||
io_service_ptr m_io_service;
|
||||
connection_hdl m_connection_hdl;
|
||||
std::vector<boost::asio::const_buffer> m_bufs;
|
||||
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
|
||||
~endpoint() {
|
||||
// clean up our io_service if we were initialized with an internal one.
|
||||
m_acceptor.reset();
|
||||
if (m_state != UNINITIALIZED && !m_external_io_service) {
|
||||
delete m_io_service;
|
||||
}
|
||||
@@ -241,8 +242,8 @@ public:
|
||||
}
|
||||
|
||||
/// wraps the run method of the internal io_service object
|
||||
void run() {
|
||||
m_io_service->run();
|
||||
std::size_t run() {
|
||||
return m_io_service->run();
|
||||
}
|
||||
|
||||
/// wraps the stop method of the internal io_service object
|
||||
@@ -250,6 +251,16 @@ public:
|
||||
m_io_service->stop();
|
||||
}
|
||||
|
||||
/// wraps the poll method of the internal io_service object
|
||||
std::size_t poll() {
|
||||
return m_io_service->poll();
|
||||
}
|
||||
|
||||
/// wraps the poll_one method of the internal io_service object
|
||||
std::size_t poll_one() {
|
||||
return m_io_service->poll_one();
|
||||
}
|
||||
|
||||
/// wraps the reset method of the internal io_service object
|
||||
void reset() {
|
||||
m_io_service->reset();
|
||||
@@ -353,20 +364,33 @@ protected:
|
||||
m_resolver.reset(new boost::asio::ip::tcp::resolver(*m_io_service));
|
||||
}
|
||||
|
||||
tcp::resolver::query query(u->get_host(),u->get_port_str());
|
||||
/*tcp::resolver::iterator iterator = resolver.resolve(query);
|
||||
std::string proxy = tcon->get_proxy();
|
||||
std::string host;
|
||||
std::string port;
|
||||
|
||||
boost::asio::async_connect(
|
||||
tcon->get_raw_socket(),
|
||||
iterator,
|
||||
lib::bind(
|
||||
&type::handle_connect,
|
||||
this, // shared from this?
|
||||
tcon,
|
||||
cb,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);*/
|
||||
if (proxy.empty()) {
|
||||
host = u->get_host();
|
||||
port = u->get_port_str();
|
||||
} else {
|
||||
try {
|
||||
lib::error_code ec;
|
||||
|
||||
uri_ptr pu(new uri(proxy));
|
||||
ec = tcon->proxy_init(u->get_host_port());
|
||||
if (ec) {
|
||||
cb(tcon->get_handle(),ec);
|
||||
return;
|
||||
}
|
||||
|
||||
host = pu->get_host();
|
||||
port = pu->get_port_str();
|
||||
} catch (uri_exception) {
|
||||
cb(tcon->get_handle(),make_error_code(error::proxy_invalid));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tcp::resolver::query query(host,port);
|
||||
|
||||
m_resolver->async_resolve(
|
||||
query,
|
||||
@@ -391,7 +415,7 @@ protected:
|
||||
std::stringstream s;
|
||||
s << "asio async_resolve error::pass_through: "
|
||||
<< "Original Error: " << ec << " (" << ec.message() << ")";
|
||||
m_elog->write(log::elevel::devel,s.str());
|
||||
m_elog->write(log::elevel::info,s.str());
|
||||
callback(tcon->get_handle(),make_error_code(error::pass_through));
|
||||
return;
|
||||
}
|
||||
@@ -418,7 +442,7 @@ protected:
|
||||
std::stringstream s;
|
||||
s << "asio async_connect error::pass_through: "
|
||||
<< "Original Error: " << ec << " (" << ec.message() << ")";
|
||||
m_elog->write(log::elevel::devel,s.str());
|
||||
m_elog->write(log::elevel::info,s.str());
|
||||
callback(tcon->get_handle(),make_error_code(error::pass_through));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,59 +50,59 @@ namespace socket {
|
||||
*/
|
||||
|
||||
namespace error {
|
||||
enum value {
|
||||
/// Catch-all error for security policy errors that don't fit in other
|
||||
/// categories
|
||||
security = 1,
|
||||
|
||||
enum value {
|
||||
/// Catch-all error for security policy errors that don't fit in other
|
||||
/// categories
|
||||
security = 1,
|
||||
|
||||
/// Catch-all error for socket component errors that don't fit in other
|
||||
/// categories
|
||||
socket,
|
||||
|
||||
/// A function was called in a state that it was illegal to do so.
|
||||
invalid_state,
|
||||
|
||||
/// The application was prompted to provide a TLS context and it was
|
||||
/// empty or otherwise invalid
|
||||
invalid_tls_context,
|
||||
|
||||
/// TLS Handshake Timeout
|
||||
tls_handshake_timeout,
|
||||
|
||||
/// pass_through from underlying library
|
||||
pass_through,
|
||||
/// A function was called in a state that it was illegal to do so.
|
||||
invalid_state,
|
||||
|
||||
/// The application was prompted to provide a TLS context and it was
|
||||
/// empty or otherwise invalid
|
||||
invalid_tls_context,
|
||||
|
||||
/// TLS Handshake Timeout
|
||||
tls_handshake_timeout,
|
||||
|
||||
/// pass_through from underlying library
|
||||
pass_through,
|
||||
|
||||
/// Required tls_init handler not present
|
||||
missing_tls_init_handler
|
||||
};
|
||||
};
|
||||
} // namespace error
|
||||
|
||||
class socket_category : public lib::error_category {
|
||||
public:
|
||||
const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
|
||||
return "websocketpp.transport.asio.socket";
|
||||
}
|
||||
|
||||
std::string message(int value) const {
|
||||
switch(value) {
|
||||
case error::security:
|
||||
return "Security policy error";
|
||||
case error::socket:
|
||||
return "Socket component error";
|
||||
case error::invalid_state:
|
||||
return "Invalid state";
|
||||
case error::invalid_tls_context:
|
||||
return "Invalid or empty TLS context supplied";
|
||||
case error::tls_handshake_timeout:
|
||||
return "TLS handshake timed out";
|
||||
case error::pass_through:
|
||||
return "Pass through from underlying library";
|
||||
const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
|
||||
return "websocketpp.transport.asio.socket";
|
||||
}
|
||||
|
||||
std::string message(int value) const {
|
||||
switch(value) {
|
||||
case error::security:
|
||||
return "Security policy error";
|
||||
case error::socket:
|
||||
return "Socket component error";
|
||||
case error::invalid_state:
|
||||
return "Invalid state";
|
||||
case error::invalid_tls_context:
|
||||
return "Invalid or empty TLS context supplied";
|
||||
case error::tls_handshake_timeout:
|
||||
return "TLS handshake timed out";
|
||||
case error::pass_through:
|
||||
return "Pass through from underlying library";
|
||||
case error::missing_tls_init_handler:
|
||||
return "Required tls_init handler not present.";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
return "Required tls_init handler not present.";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline const lib::error_category& get_socket_category() {
|
||||
@@ -111,7 +111,7 @@ inline const lib::error_category& get_socket_category() {
|
||||
}
|
||||
|
||||
inline lib::error_code make_error(error::value e) {
|
||||
return lib::error_code(static_cast<int>(e), get_socket_category());
|
||||
return lib::error_code(static_cast<int>(e), get_socket_category());
|
||||
}
|
||||
|
||||
typedef lib::function<void(const lib::error_code&)> init_handler;
|
||||
|
||||
@@ -51,28 +51,28 @@ typedef lib::function<void(connection_hdl,boost::asio::ip::tcp::socket&)>
|
||||
class connection {
|
||||
public:
|
||||
/// Type of this connection socket component
|
||||
typedef connection type;
|
||||
typedef connection type;
|
||||
/// Type of a shared pointer to this connection socket component
|
||||
typedef lib::shared_ptr<type> ptr;
|
||||
|
||||
/// Type of a pointer to the ASIO io_service being used
|
||||
typedef boost::asio::io_service* io_service_ptr;
|
||||
typedef boost::asio::io_service* io_service_ptr;
|
||||
/// Type of a shared pointer to the socket being used.
|
||||
typedef lib::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr;
|
||||
|
||||
explicit connection() : m_state(UNINITIALIZED) {
|
||||
//std::cout << "transport::asio::basic_socket::connection constructor"
|
||||
|
||||
explicit connection() : m_state(UNINITIALIZED) {
|
||||
//std::cout << "transport::asio::basic_socket::connection constructor"
|
||||
// << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Check whether or not this connection is secure
|
||||
/**
|
||||
* @return Wether or not this connection is secure
|
||||
*/
|
||||
bool is_secure() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_secure() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Set the socket initialization handler
|
||||
/**
|
||||
* The socket initialization handler is called after the socket object is
|
||||
@@ -85,59 +85,95 @@ public:
|
||||
m_socket_init_handler = h;
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the underlying socket
|
||||
/**
|
||||
* This is used internally. It can also be used to set socket options, etc
|
||||
*/
|
||||
/// Retrieve a pointer to the underlying socket
|
||||
/**
|
||||
* This is used internally. It can also be used to set socket options, etc
|
||||
*/
|
||||
boost::asio::ip::tcp::socket& get_socket() {
|
||||
return *m_socket;
|
||||
return *m_socket;
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the underlying socket
|
||||
/**
|
||||
* This is used internally. It can also be used to set socket options, etc
|
||||
*/
|
||||
/**
|
||||
* This is used internally.
|
||||
*/
|
||||
boost::asio::ip::tcp::socket& get_next_layer() {
|
||||
return *m_socket;
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the underlying socket
|
||||
/**
|
||||
* This is used internally. It can also be used to set socket options, etc
|
||||
*/
|
||||
boost::asio::ip::tcp::socket& get_raw_socket() {
|
||||
return *m_socket;
|
||||
return *m_socket;
|
||||
}
|
||||
|
||||
/// Get the remote endpoint address
|
||||
/**
|
||||
* The iostream transport has no information about the ultimate remote
|
||||
* endpoint. It will return the string "iostream transport". To indicate
|
||||
* this.
|
||||
*
|
||||
* TODO: allow user settable remote endpoint addresses if this seems useful
|
||||
*
|
||||
* @return A string identifying the address of the remote endpoint
|
||||
*/
|
||||
std::string get_remote_endpoint(lib::error_code &ec) const {
|
||||
std::stringstream s;
|
||||
|
||||
boost::system::error_code bec;
|
||||
boost::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(bec);
|
||||
|
||||
if (bec) {
|
||||
ec = error::make_error_code(error::pass_through);
|
||||
s << "Error getting remote endpoint: " << bec
|
||||
<< " (" << bec.message() << ")";
|
||||
return s.str();
|
||||
} else {
|
||||
ec = lib::error_code();
|
||||
s << ep;
|
||||
return s.str();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
/// Perform one time initializations
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*
|
||||
* @param service A pointer to the endpoint's io_service
|
||||
* @param strand A shared pointer to the connection's asio strand
|
||||
* @param is_server Whether or not the endpoint is a server or not.
|
||||
*/
|
||||
/// Perform one time initializations
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*
|
||||
* @param service A pointer to the endpoint's io_service
|
||||
* @param strand A shared pointer to the connection's asio strand
|
||||
* @param is_server Whether or not the endpoint is a server or not.
|
||||
*/
|
||||
lib::error_code init_asio (io_service_ptr service, bool is_server) {
|
||||
if (m_state != UNINITIALIZED) {
|
||||
return socket::make_error(socket::error::invalid_state);
|
||||
}
|
||||
|
||||
m_socket.reset(new boost::asio::ip::tcp::socket(*service));
|
||||
|
||||
m_state = READY;
|
||||
|
||||
return lib::error_code();
|
||||
if (m_state != UNINITIALIZED) {
|
||||
return socket::make_error(socket::error::invalid_state);
|
||||
}
|
||||
|
||||
m_socket.reset(new boost::asio::ip::tcp::socket(*service));
|
||||
|
||||
m_state = READY;
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
/// Initialize security policy for reading
|
||||
void init(init_handler callback) {
|
||||
if (m_state != READY) {
|
||||
callback(socket::make_error(socket::error::invalid_state));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// Initialize security policy for reading
|
||||
void init(init_handler callback) {
|
||||
if (m_state != READY) {
|
||||
callback(socket::make_error(socket::error::invalid_state));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_socket_init_handler) {
|
||||
m_socket_init_handler(m_hdl,*m_socket);
|
||||
}
|
||||
|
||||
m_state = READING;
|
||||
|
||||
callback(lib::error_code());
|
||||
}
|
||||
|
||||
|
||||
m_state = READING;
|
||||
|
||||
callback(lib::error_code());
|
||||
}
|
||||
|
||||
/// Sets the connection handle
|
||||
/**
|
||||
* The connection handle is passed to any handlers to identify the
|
||||
@@ -156,14 +192,14 @@ protected:
|
||||
// TODO: handle errors
|
||||
}
|
||||
private:
|
||||
enum state {
|
||||
UNINITIALIZED = 0,
|
||||
READY = 1,
|
||||
READING = 2
|
||||
};
|
||||
|
||||
socket_ptr m_socket;
|
||||
state m_state;
|
||||
enum state {
|
||||
UNINITIALIZED = 0,
|
||||
READY = 1,
|
||||
READING = 2
|
||||
};
|
||||
|
||||
socket_ptr m_socket;
|
||||
state m_state;
|
||||
|
||||
connection_hdl m_hdl;
|
||||
socket_init_handler m_socket_init_handler;
|
||||
@@ -185,10 +221,7 @@ public:
|
||||
/// component.
|
||||
typedef socket_con_type::ptr socket_con_ptr;
|
||||
|
||||
explicit endpoint() {
|
||||
std::cout << "transport::asio::basic_socket::endpoint constructor"
|
||||
<< std::endl;
|
||||
}
|
||||
explicit endpoint() {}
|
||||
|
||||
/// Checks whether the endpoint creates secure connections
|
||||
/**
|
||||
|
||||
@@ -58,51 +58,59 @@ typedef lib::function<lib::shared_ptr<boost::asio::ssl::context>(connection_hdl)
|
||||
class connection {
|
||||
public:
|
||||
/// Type of this connection socket component
|
||||
typedef connection type;
|
||||
/// Type of a shared pointer to this connection socket component
|
||||
typedef connection type;
|
||||
/// Type of a shared pointer to this connection socket component
|
||||
typedef lib::shared_ptr<type> ptr;
|
||||
|
||||
/// Type of the ASIO socket being used
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_type;
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_type;
|
||||
/// Type of a shared pointer to the ASIO socket being used
|
||||
typedef lib::shared_ptr<socket_type> socket_ptr;
|
||||
/// Type of a pointer to the ASIO io_service being used
|
||||
typedef boost::asio::io_service* io_service_ptr;
|
||||
typedef boost::asio::io_service* io_service_ptr;
|
||||
/// Type of a shared pointer to the ASIO TLS context being used
|
||||
typedef lib::shared_ptr<boost::asio::ssl::context> context_ptr;
|
||||
typedef lib::shared_ptr<boost::asio::ssl::context> context_ptr;
|
||||
/// Type of a shared pointer to the ASIO timer being used
|
||||
typedef lib::shared_ptr<boost::asio::deadline_timer> timer_ptr;
|
||||
|
||||
typedef boost::system::error_code boost_error;
|
||||
|
||||
explicit connection() {
|
||||
//std::cout << "transport::asio::tls_socket::connection constructor"
|
||||
typedef lib::shared_ptr<boost::asio::deadline_timer> timer_ptr;
|
||||
|
||||
typedef boost::system::error_code boost_error;
|
||||
|
||||
explicit connection() {
|
||||
//std::cout << "transport::asio::tls_socket::connection constructor"
|
||||
// << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Check whether or not this connection is secure
|
||||
/**
|
||||
* @return Wether or not this connection is secure
|
||||
*/
|
||||
bool is_secure() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the underlying socket
|
||||
/**
|
||||
* This is used internally. It can also be used to set socket options, etc
|
||||
*/
|
||||
socket_type::lowest_layer_type& get_raw_socket() {
|
||||
return m_socket->lowest_layer();
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the wrapped socket
|
||||
/**
|
||||
* This is used internally.
|
||||
*/
|
||||
socket_type& get_socket() {
|
||||
return *m_socket;
|
||||
}
|
||||
bool is_secure() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the underlying socket
|
||||
/**
|
||||
* This is used internally. It can also be used to set socket options, etc
|
||||
*/
|
||||
socket_type::lowest_layer_type& get_raw_socket() {
|
||||
return m_socket->lowest_layer();
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the layer below the ssl stream
|
||||
/**
|
||||
* This is used internally.
|
||||
*/
|
||||
socket_type::next_layer_type& get_next_layer() {
|
||||
return m_socket->next_layer();
|
||||
}
|
||||
|
||||
/// Retrieve a pointer to the wrapped socket
|
||||
/**
|
||||
* This is used internally.
|
||||
*/
|
||||
socket_type& get_socket() {
|
||||
return *m_socket;
|
||||
}
|
||||
|
||||
/// Set the socket initialization handler
|
||||
/**
|
||||
@@ -128,51 +136,77 @@ public:
|
||||
void set_tls_init_handler(tls_init_handler h) {
|
||||
m_tls_init_handler = h;
|
||||
}
|
||||
|
||||
/// Get the remote endpoint address
|
||||
/**
|
||||
* The iostream transport has no information about the ultimate remote
|
||||
* endpoint. It will return the string "iostream transport". To indicate
|
||||
* this.
|
||||
*
|
||||
* TODO: allow user settable remote endpoint addresses if this seems useful
|
||||
*
|
||||
* @return A string identifying the address of the remote endpoint
|
||||
*/
|
||||
std::string get_remote_endpoint(lib::error_code &ec) const {
|
||||
std::stringstream s;
|
||||
|
||||
boost::system::error_code bec;
|
||||
boost::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(bec);
|
||||
|
||||
if (bec) {
|
||||
ec = error::make_error_code(error::pass_through);
|
||||
s << "Error getting remote endpoint: " << bec
|
||||
<< " (" << bec.message() << ")";
|
||||
return s.str();
|
||||
} else {
|
||||
ec = lib::error_code();
|
||||
s << ep;
|
||||
return s.str();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
/// Perform one time initializations
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*
|
||||
* @param service A pointer to the endpoint's io_service
|
||||
* @param strand A shared pointer to the connection's asio strand
|
||||
* @param is_server Whether or not the endpoint is a server or not.
|
||||
*/
|
||||
/// Perform one time initializations
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service
|
||||
*
|
||||
* @param service A pointer to the endpoint's io_service
|
||||
* @param strand A shared pointer to the connection's asio strand
|
||||
* @param is_server Whether or not the endpoint is a server or not.
|
||||
*/
|
||||
lib::error_code init_asio (io_service_ptr service, bool is_server) {
|
||||
std::cout << "transport::security::tls_socket::init_asio" << std::endl;
|
||||
if (!m_tls_init_handler) {
|
||||
std::cout << "missing_tls_init_handler" << std::endl;
|
||||
return socket::make_error(socket::error::missing_tls_init_handler);
|
||||
return socket::make_error(socket::error::missing_tls_init_handler);
|
||||
}
|
||||
m_context = m_tls_init_handler(m_hdl);
|
||||
|
||||
if (!m_context) {
|
||||
return socket::make_error(socket::error::invalid_tls_context);
|
||||
}
|
||||
|
||||
m_socket.reset(new socket_type(*service,*m_context));
|
||||
|
||||
m_timer.reset(new boost::asio::deadline_timer(
|
||||
*service,
|
||||
boost::posix_time::seconds(0))
|
||||
);
|
||||
|
||||
m_io_service = service;
|
||||
m_is_server = is_server;
|
||||
|
||||
return lib::error_code();
|
||||
|
||||
if (!m_context) {
|
||||
return socket::make_error(socket::error::invalid_tls_context);
|
||||
}
|
||||
|
||||
m_socket.reset(new socket_type(*service,*m_context));
|
||||
|
||||
m_timer.reset(new boost::asio::deadline_timer(
|
||||
*service,
|
||||
boost::posix_time::seconds(0))
|
||||
);
|
||||
|
||||
m_io_service = service;
|
||||
m_is_server = is_server;
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
/// Initialize security policy for reading
|
||||
void init(init_handler callback) {
|
||||
if (m_socket_init_handler) {
|
||||
|
||||
/// Initialize security policy for reading
|
||||
void init(init_handler callback) {
|
||||
if (m_socket_init_handler) {
|
||||
m_socket_init_handler(m_hdl,get_raw_socket());
|
||||
}
|
||||
|
||||
// register timeout
|
||||
m_timer->expires_from_now(boost::posix_time::milliseconds(5000));
|
||||
// TEMP
|
||||
m_timer->async_wait(
|
||||
// register timeout
|
||||
m_timer->expires_from_now(boost::posix_time::milliseconds(5000));
|
||||
// TEMP
|
||||
m_timer->async_wait(
|
||||
lib::bind(
|
||||
&type::handle_timeout,
|
||||
this,
|
||||
@@ -180,19 +214,19 @@ protected:
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
|
||||
// TLS handshake
|
||||
m_socket->async_handshake(
|
||||
get_handshake_type(),
|
||||
lib::bind(
|
||||
&type::handle_init,
|
||||
this,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// TLS handshake
|
||||
m_socket->async_handshake(
|
||||
get_handshake_type(),
|
||||
lib::bind(
|
||||
&type::handle_init,
|
||||
this,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// Sets the connection handle
|
||||
/**
|
||||
* The connection handle is passed to any handlers to identify the
|
||||
@@ -204,39 +238,39 @@ protected:
|
||||
m_hdl = hdl;
|
||||
}
|
||||
|
||||
void handle_timeout(init_handler callback, const
|
||||
boost::system::error_code& error)
|
||||
{
|
||||
if (error) {
|
||||
if (error.value() == boost::asio::error::operation_aborted) {
|
||||
// The timer was cancelled because the handshake succeeded.
|
||||
return;
|
||||
}
|
||||
|
||||
// Some other ASIO error, pass it through
|
||||
// TODO: make this error pass through better
|
||||
callback(socket::make_error(socket::error::pass_through));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(socket::make_error(socket::error::tls_handshake_timeout));
|
||||
}
|
||||
|
||||
void handle_init(init_handler callback, const
|
||||
boost::system::error_code& error)
|
||||
{
|
||||
/// stop waiting for our handshake timer.
|
||||
m_timer->cancel();
|
||||
|
||||
if (error) {
|
||||
// TODO: make this error pass through better
|
||||
callback(socket::make_error(socket::error::pass_through));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(lib::error_code());
|
||||
}
|
||||
|
||||
void handle_timeout(init_handler callback, const
|
||||
boost::system::error_code& error)
|
||||
{
|
||||
if (error) {
|
||||
if (error.value() == boost::asio::error::operation_aborted) {
|
||||
// The timer was cancelled because the handshake succeeded.
|
||||
return;
|
||||
}
|
||||
|
||||
// Some other ASIO error, pass it through
|
||||
// TODO: make this error pass through better
|
||||
callback(socket::make_error(socket::error::pass_through));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(socket::make_error(socket::error::tls_handshake_timeout));
|
||||
}
|
||||
|
||||
void handle_init(init_handler callback, const
|
||||
boost::system::error_code& error)
|
||||
{
|
||||
/// stop waiting for our handshake timer.
|
||||
m_timer->cancel();
|
||||
|
||||
if (error) {
|
||||
// TODO: make this error pass through better
|
||||
callback(socket::make_error(socket::error::pass_through));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(lib::error_code());
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
boost::system::error_code ec;
|
||||
m_socket->shutdown(ec);
|
||||
@@ -244,19 +278,19 @@ protected:
|
||||
// TODO: error handling
|
||||
}
|
||||
private:
|
||||
socket_type::handshake_type get_handshake_type() {
|
||||
socket_type::handshake_type get_handshake_type() {
|
||||
if (m_is_server) {
|
||||
return boost::asio::ssl::stream_base::server;
|
||||
} else {
|
||||
return boost::asio::ssl::stream_base::client;
|
||||
}
|
||||
}
|
||||
|
||||
io_service_ptr m_io_service;
|
||||
context_ptr m_context;
|
||||
socket_ptr m_socket;
|
||||
timer_ptr m_timer;
|
||||
bool m_is_server;
|
||||
|
||||
io_service_ptr m_io_service;
|
||||
context_ptr m_context;
|
||||
socket_ptr m_socket;
|
||||
timer_ptr m_timer;
|
||||
bool m_is_server;
|
||||
|
||||
connection_hdl m_hdl;
|
||||
socket_init_handler m_socket_init_handler;
|
||||
@@ -279,10 +313,7 @@ public:
|
||||
/// component.
|
||||
typedef socket_con_type::ptr socket_con_ptr;
|
||||
|
||||
explicit endpoint() {
|
||||
std::cout << "transport::asio::tls_socket::endpoint constructor"
|
||||
<< std::endl;
|
||||
}
|
||||
explicit endpoint() {}
|
||||
|
||||
/// Checks whether the endpoint creates secure connections
|
||||
/**
|
||||
@@ -323,7 +354,6 @@ protected:
|
||||
* the socket component of the connection.
|
||||
*/
|
||||
void init(socket_con_ptr scon) {
|
||||
std::cout << "transport::asio::tls_socket::init" << std::endl;
|
||||
scon->set_socket_init_handler(m_socket_init_handler);
|
||||
scon->set_tls_init_handler(m_tls_init_handler);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,10 @@ enum value {
|
||||
operation_aborted,
|
||||
|
||||
/// Operation not supported
|
||||
operation_not_supported
|
||||
operation_not_supported,
|
||||
|
||||
/// End of file
|
||||
eof
|
||||
};
|
||||
|
||||
class category : public lib::error_category {
|
||||
@@ -124,6 +127,8 @@ class category : public lib::error_category {
|
||||
return "The operation was aborted";
|
||||
case operation_not_supported:
|
||||
return "The operation is not supported by this transport";
|
||||
case eof:
|
||||
return "End of File";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -112,6 +112,22 @@ public:
|
||||
return in;
|
||||
}
|
||||
|
||||
/// Manual input supply
|
||||
/**
|
||||
* Copies bytes from buf into WebSocket++'s input buffers. Bytes will be
|
||||
* copied from the supplied buffer to fullfull any pending library reads. It
|
||||
* will return the number of bytes successfully processed. If there are no
|
||||
* pending reads readsome will return immediately. Not all of the bytes may
|
||||
* be able to be read in one call
|
||||
*/
|
||||
size_t readsome(const char *buf, size_t len) {
|
||||
// this serializes calls to external read.
|
||||
scoped_lock_type lock(m_read_mutex);
|
||||
|
||||
return this->readsome_impl(buf,len);
|
||||
}
|
||||
|
||||
|
||||
/// Tests whether or not the underlying transport is secure
|
||||
/**
|
||||
* iostream transport will return false always because it has no information
|
||||
@@ -304,20 +320,20 @@ private:
|
||||
void read(std::istream &in) {
|
||||
m_alog.write(log::alevel::devel,"iostream_con read");
|
||||
|
||||
while (in.good()) {
|
||||
if (!m_reading) {
|
||||
while (in.good()) {
|
||||
if (!m_reading) {
|
||||
m_elog.write(log::elevel::devel,"write while not reading");
|
||||
break;
|
||||
}
|
||||
|
||||
in.read(m_buf+m_cursor,m_len-m_cursor);
|
||||
}
|
||||
|
||||
in.read(m_buf+m_cursor,static_cast<std::streamsize>(m_len-m_cursor));
|
||||
|
||||
if (in.gcount() == 0) {
|
||||
m_elog.write(log::elevel::devel,"read zero bytes");
|
||||
break;
|
||||
}
|
||||
|
||||
m_cursor += in.gcount();
|
||||
m_cursor += static_cast<size_t>(in.gcount());
|
||||
|
||||
// TODO: error handling
|
||||
if (in.bad()) {
|
||||
@@ -332,6 +348,28 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
size_t readsome_impl(const char * buf, size_t len) {
|
||||
m_alog.write(log::alevel::devel,"iostream_con readsome");
|
||||
|
||||
if (!m_reading) {
|
||||
m_elog.write(log::elevel::devel,"write while not reading");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bytes_to_copy = std::min(len,m_len-m_cursor);
|
||||
|
||||
std::copy(buf,buf+bytes_to_copy,m_buf);
|
||||
|
||||
m_cursor += bytes_to_copy;
|
||||
|
||||
if (m_cursor >= m_bytes_needed) {
|
||||
m_reading = false;
|
||||
m_read_handler(lib::error_code(), m_cursor);
|
||||
}
|
||||
|
||||
return bytes_to_copy;
|
||||
}
|
||||
|
||||
// Read space (Protected by m_read_mutex)
|
||||
char* m_buf;
|
||||
size_t m_len;
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
// generate and manage our own io_service
|
||||
explicit endpoint() : output_stream(NULL)
|
||||
{
|
||||
std::cout << "transport::iostream::endpoint constructor" << std::endl;
|
||||
//std::cout << "transport::iostream::endpoint constructor" << std::endl;
|
||||
}
|
||||
|
||||
void register_ostream(std::ostream* o) {
|
||||
|
||||
@@ -61,6 +61,77 @@ static const uint16_t uri_default_secure_port = 443;
|
||||
class uri {
|
||||
public:
|
||||
explicit uri(const std::string& uri) {
|
||||
// TODO: should this split resource into path/query?
|
||||
lib::cmatch matches;
|
||||
const lib::regex expression("(http|ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:.]+\\])(:\\d{1,5})?(/[^#]*)?");
|
||||
|
||||
if (lib::regex_match(uri.c_str(), matches, expression)) {
|
||||
m_secure = (matches[1] == "wss");
|
||||
m_host = matches[2];
|
||||
|
||||
// strip brackets from IPv6 literal URIs
|
||||
if (m_host[0] == '[') {
|
||||
m_host = m_host.substr(1,m_host.size()-2);
|
||||
}
|
||||
|
||||
std::string port(matches[3]);
|
||||
|
||||
if (port != "") {
|
||||
// strip off the :
|
||||
// this could probably be done with a better regex.
|
||||
port = port.substr(1);
|
||||
}
|
||||
|
||||
m_port = get_port_from_string(port);
|
||||
|
||||
m_resource = matches[4];
|
||||
|
||||
if (m_resource == "") {
|
||||
m_resource = "/";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw websocketpp::uri_exception("Error parsing WebSocket URI");
|
||||
}
|
||||
|
||||
/*explicit uri(const std::string& uri) {
|
||||
// test for ws or wss
|
||||
std::string::const_iterator it;
|
||||
std::string::const_iterator temp;
|
||||
|
||||
it = uri.begin();
|
||||
|
||||
if (std::equal(it,it+6,"wss://")) {
|
||||
m_secure = true;
|
||||
it += 6;
|
||||
} else if (std::equal(it,it+5,"ws://")) {
|
||||
m_secure = false;
|
||||
it += 5;
|
||||
} else {
|
||||
// error
|
||||
}
|
||||
|
||||
// extract host.
|
||||
// either a host string
|
||||
// an IPv4 address
|
||||
// or an IPv6 address
|
||||
if (*it == '[') {
|
||||
++it;
|
||||
// IPv6 literal
|
||||
// extract IPv6 digits until ]
|
||||
temp = std::find(it,uri.end(),']');
|
||||
if (temp == uri.end()) {
|
||||
// error
|
||||
} else {
|
||||
// validate IPv6 literal parts
|
||||
// can contain numbers, a-f and A-F
|
||||
}
|
||||
} else {
|
||||
// IPv4 or hostname
|
||||
}
|
||||
|
||||
// TODO: should this split resource into path/query?
|
||||
lib::cmatch matches;
|
||||
const lib::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:.]+\\])(:\\d{1,5})?(/[^#]*)?");
|
||||
@@ -95,7 +166,7 @@ public:
|
||||
|
||||
throw websocketpp::uri_exception("Error parsing WebSocket URI");
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
uri(bool secure, const std::string& host, uint16_t port, const std::string& resource)
|
||||
: m_host(host)
|
||||
@@ -184,7 +255,7 @@ private:
|
||||
return (m_secure ? uri_default_secure_port : uri_default_port);
|
||||
}
|
||||
|
||||
unsigned int t_port = atoi(port.c_str());
|
||||
unsigned int t_port = static_cast<unsigned int>(atoi(port.c_str()));
|
||||
|
||||
if (t_port > 65535) {
|
||||
throw websocketpp::uri_exception("Port must be less than 65535");
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
template <typename iterator_type>
|
||||
bool decode (iterator_type b, iterator_type e) {
|
||||
for (iterator_type i = b; i != e; i++) {
|
||||
if (utf8_validator::decode(&m_state,&m_codepoint,*i) == UTF8_REJECT) {
|
||||
if (utf8_validator::decode(&m_state,&m_codepoint,static_cast<uint8_t>(*i)) == UTF8_REJECT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +68,8 @@ typename T::const_iterator ci_find_substr(const T& str1,
|
||||
|
||||
|
||||
|
||||
/// Host to network long long
|
||||
//uint64_t htonll(uint64_t src);
|
||||
/// Network to host long long
|
||||
//uint64_t ntohll(uint64_t src);
|
||||
std::string string_replace_all(std::string subject, const std::string& search,
|
||||
const std::string& replace);
|
||||
|
||||
/// Convert std::string to ascii printed string of hex digits
|
||||
std::string to_hex(const std::string& input);
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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_HPP
|
||||
#define WEBSOCKETPP_HPP
|
||||
|
||||
/*
|
||||
|
||||
Endpoint
|
||||
- container for connections
|
||||
- Store and forward default connection settings
|
||||
|
||||
Connection
|
||||
- Represents the state and functionality of a single WebSocket session starting
|
||||
with the opening handshake and completing with the closing one.
|
||||
- After a connection is created settings may be applied that will be used for
|
||||
this connection.
|
||||
- Once setup is complete a start method is run and the connection enters its
|
||||
event loop. The connection requests bytes from its transport, then runs those
|
||||
bytes through the appropriate websocket frame processor, and calls handler
|
||||
methods appropriate for the types of frames recieved.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Policies:
|
||||
|
||||
Concurrency
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Concurrency Models
|
||||
Single Thread Async (lock free)
|
||||
- WS++ runs lock free (Access to endpoint and connection from non-WS++ threads is unsafe)
|
||||
- All handlers and networking operations run in a single thread.
|
||||
- Handlers can block each other and network operations
|
||||
- Good for low traffic workflows where connections are independent and requests are short.
|
||||
|
||||
Single Thread Async
|
||||
- Same as lock free version except access to endpoint and connection from non-WS++ threads is safe
|
||||
- Good for workflows where any long running handler job is deferred to a non-WS++ thread for processing.
|
||||
|
||||
Thread Pool (lock free)
|
||||
- WS++ runs lock free (access to endpoint and connection from non-WS++ threads is unsafe)
|
||||
- Handlers and networking operations invoked by multiple threads. Individual connections are serialized.
|
||||
- n handlers will block network operations (n=num_threads)
|
||||
- Allows much better multi-core utilization, does not require end user syncronization as long as all work is performed inside handlers and handlers only reference their own connection. Handler local data must be syncronized.
|
||||
|
||||
Thread pool
|
||||
- Same as lock free version except access to endpoint and connection from non-WS++ threads is safe.
|
||||
|
||||
Thread per connection
|
||||
-
|
||||
|
||||
io_service policies
|
||||
- external vs internal
|
||||
- per endpoint or per connection
|
||||
|
||||
|
||||
message policies?
|
||||
- Control Messages:
|
||||
-- Each connection should have a single control message permanently allocated
|
||||
- Data Messages
|
||||
-- Dynamically allocate a new data message as needed.
|
||||
-- Re-usable pool of data messages per endpoint
|
||||
-- Re-usable pool of data messages per connection
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <websocketpp/common.hpp>
|
||||
#include <websocketpp/concurrency/async.hpp>
|
||||
|
||||
#include <websocketpp/connection.hpp>
|
||||
#include <websocketpp/endpoint.hpp>
|
||||
|
||||
#endif // WEBSOCKETPP_ENDPOINT_HPP
|
||||
Reference in New Issue
Block a user