Merge remote-tracking branch 'upstream/0.3.x-cmake' into cmake_prebuild_system

Conflicts:
	websocketpp/endpoint.hpp
This commit is contained in:
unknown
2013-05-04 02:07:17 +03:00
78 changed files with 3481 additions and 972 deletions

2
.gitignore vendored
View File

@@ -75,3 +75,5 @@ build/
examples/wsperf/wsperf_client
*.out
*.log

View File

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

View File

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

View 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')

View 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();
}

View 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')

View 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();
}

View 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')

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

View 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')

View 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);
}

View 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')

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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&paramsXML=string");
std::cout << r.raw() << std::endl;
BOOST_CHECK( r.raw() == raw );
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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" );
}*/

View File

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

View File

@@ -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);
}*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
// ( ) < > @ , ; : \ " / [ ] ? = { }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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