Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6fd37ae05 | ||
|
|
2f2213f562 | ||
|
|
9d51669fb8 | ||
|
|
01969452fd | ||
|
|
c0a28e27aa | ||
|
|
a48fc73edb | ||
|
|
531a98fcad | ||
|
|
fc0a8755ad | ||
|
|
b783abe6c6 | ||
|
|
0e00cdcfee | ||
|
|
382b4433cf | ||
|
|
2ceac23e60 | ||
|
|
7c6b781433 | ||
|
|
3597ab0a26 | ||
|
|
6f46a2c2da | ||
|
|
1b85ae7a9d | ||
|
|
195a29dc46 | ||
|
|
2ff367514f |
No files matched your search
-11
@@ -79,8 +79,6 @@ elif env['PLATFORM'] == 'posix':
|
||||
env.Append(CCFLAGS = ['-Wall'])
|
||||
#env['LINKFLAGS'] = ''
|
||||
elif env['PLATFORM'] == 'darwin':
|
||||
if not os.environ.has_key('CXX'):
|
||||
env['CXX'] = "clang++"
|
||||
if env.has_key('DEBUG'):
|
||||
env.Append(CCFLAGS = ['-g', '-O0'])
|
||||
else:
|
||||
@@ -100,13 +98,11 @@ else:
|
||||
if env['CXX'].startswith('g++'):
|
||||
#env.Append(CCFLAGS = ['-Wconversion'])
|
||||
env.Append(CCFLAGS = ['-Wcast-align'])
|
||||
env.Append(CCFLAGS = ['-Wshadow'])
|
||||
elif env['CXX'].startswith('clang++'):
|
||||
#env.Append(CCFLAGS = ['-Wcast-align'])
|
||||
#env.Append(CCFLAGS = ['-Wglobal-constructors'])
|
||||
#env.Append(CCFLAGS = ['-Wconversion'])
|
||||
env.Append(CCFLAGS = ['-Wno-padded'])
|
||||
env.Append(CCFLAGS = ['-Wshadow'])
|
||||
|
||||
# Wpadded
|
||||
# Wsign-conversion
|
||||
@@ -146,7 +142,6 @@ if env_cpp11['CXX'].startswith('g++'):
|
||||
print "C++11 build environment is not supported on this version of G++"
|
||||
elif env_cpp11['CXX'].startswith('clang++'):
|
||||
print "C++11 build environment enabled"
|
||||
env.Append(CXXFLANGS = ['-stdlib=libc++'],LINKFLAGS=['-stdlib=libc++'])
|
||||
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
|
||||
@@ -227,12 +222,6 @@ testee_client = SConscript('#/examples/testee_client/SConscript',variant_dir = b
|
||||
# utility_client
|
||||
utility_client = SConscript('#/examples/utility_client/SConscript',variant_dir = builddir + 'utility_client',duplicate = 0)
|
||||
|
||||
# debug_client
|
||||
debug_client = SConscript('#/examples/debug_client/SConscript',variant_dir = builddir + 'debug_client',duplicate = 0)
|
||||
|
||||
# debug_server
|
||||
debug_server = SConscript('#/examples/debug_server/SConscript',variant_dir = builddir + 'debug_server',duplicate = 0)
|
||||
|
||||
# subprotocol_server
|
||||
subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',variant_dir = builddir + 'subprotocol_server',duplicate = 0)
|
||||
|
||||
|
||||
+8
-60
@@ -1,64 +1,12 @@
|
||||
HEAD
|
||||
- Feature: Adds `start_perpetual` and `stop_perpetual` methods to asio transport
|
||||
These may be used to replace manually managed `asio::io_service::work` objects
|
||||
- Feature: Allow setting pong and handshake timeouts at runtime.
|
||||
- Feature: Allows changing the listen backlog queue length.
|
||||
- Feature: Split tcp init into pre and post init.
|
||||
- Feature: Adds URI method to extract query string from URI. Thank you Banaan
|
||||
for code. #298
|
||||
- Feature: Adds a compile time switch to asio transport config to disable
|
||||
certain multithreading features (some locks, asio strands)
|
||||
- Feature: Adds the ability to pause reading on a connection. Paused connections
|
||||
will not read more data from their socket, allowing TCP flow control to work
|
||||
without blocking the main thread.
|
||||
- Feature: Adds the ability to specify whether or not to use the `SO_REUSEADDR`
|
||||
TCP socket option. The default for this value has been changed from `true` to
|
||||
`false`.
|
||||
- Feature: Adds the ability to specify a maximum message size.
|
||||
- Feature: Adds `close::status::get_string(...)` method to look up a human
|
||||
readable string given a close code value.
|
||||
- Improvement: Open, close, and pong timeouts can be disabled entirely by
|
||||
setting their duration to 0.
|
||||
- Improvement: Numerous performance improvements. Including: tuned default
|
||||
buffer sizes based on profiling, caching of handler binding for async
|
||||
reads/writes, non-malloc allocators for read/write handlers, disabling of a
|
||||
number of questionably useful range sanity checks in tight inner loops.
|
||||
- Improvement: Cleaned up the handling of TLS related errors. TLS errors will
|
||||
now be reported with more detail on the info channel rather than all being
|
||||
`tls_short_read` or `pass_through`. In addition, many cases where a TLS short
|
||||
read was in fact expected are no longer classified as errors. Expected TLS
|
||||
short reads and quasi-expected socket shutdown related errors will no longer
|
||||
be reported as unclean WebSocket shutdowns to the application. Information
|
||||
about them will remain in the info error channel for debugging purposes.
|
||||
- Improvement: `start_accept` and `listen` errors are now reported to the caller
|
||||
either via an exception or an ec parameter.
|
||||
- Improvement: Outgoing writes are now batched for improved message throughput
|
||||
and reduced system call and TCP frame overhead.
|
||||
- Bug: Fix some cases of calls to empty lib::function objects.
|
||||
- Bug: Fix memory leak of connection objects due to cached handlers holding on to
|
||||
reference counted pointers. #310 Thank you otaras for reporting.
|
||||
- Bug: Fix issue with const endpoint accessors (such as `get_user_agent`) not
|
||||
compiling due to non-const mutex use. #292 Thank you logofive for reporting.
|
||||
- Bug: Fix handler allocation crash with multithreaded `io_service`.
|
||||
- Bug: Fixes incorrect whitespace handling in header parsing. #301 Thank you
|
||||
Wolfram Schroers for reporting
|
||||
- Bug: Fix a crash when parsing empty HTTP headers. Thank you Thingol for
|
||||
reporting.
|
||||
- Bug: Fix a crash following use of the `stop_listening` function. Thank you
|
||||
Thingol for reporting.
|
||||
- Bug: Fix use of variable names that shadow function parameters. The library
|
||||
should compile cleanly with -Wshadow now. Thank you giszo for reporting. #318
|
||||
- Bug: Fix an issue where `set_open_handshake_timeout` was ignored by server
|
||||
code. Thank you Robin Rowe for reporting.
|
||||
- Bug: Fix an issue where custom timeout values weren't being propagated from
|
||||
endpoints to new connections.
|
||||
- Bug: Fix a number of memory leaks related to server connection failures. #323
|
||||
#333 #334 #335 Thank you droppy and aydany for reporting and patches.
|
||||
reporting.
|
||||
- Compatibility: Fix compile time conflict with Visual Studio's MIN/MAX macros.
|
||||
Thank you Robin Rowe for reporting.
|
||||
- Documentation: Examples and test suite build system now defaults to clang on
|
||||
OS X
|
||||
- Adds URI method to extract query string from URI. Thank you Banaan for code.
|
||||
#298
|
||||
- Numerous performance improvements. Including: tuned default buffer sizes based
|
||||
on profiling, caching of handler binding for async reads/writes, non-malloc
|
||||
allocators for read/write handlers, disabling of a number of questionably
|
||||
useful range sanity checks in tight inner loops.
|
||||
- Adds a compile time switch to asio transport config to disable certain
|
||||
multithreading features (some locks, asio strands)
|
||||
|
||||
0.3.0-alpha4 - 2013-10-11
|
||||
- HTTP requests ending normally are no longer logged as errors. Thank you Banaan
|
||||
|
||||
@@ -34,8 +34,7 @@ enum action_type {
|
||||
|
||||
struct action {
|
||||
action(action_type t, connection_hdl h) : type(t), hdl(h) {}
|
||||
action(action_type t, connection_hdl h, server::message_ptr m)
|
||||
: type(t), hdl(h), msg(m) {}
|
||||
action(action_type t, server::message_ptr m) : type(t), msg(m) {}
|
||||
|
||||
action_type type;
|
||||
websocketpp::connection_hdl hdl;
|
||||
@@ -93,7 +92,7 @@ public:
|
||||
// queue message up for sending by processing thread
|
||||
unique_lock<mutex> lock(m_action_lock);
|
||||
//std::cout << "on_message" << std::endl;
|
||||
m_actions.push(action(MESSAGE,hdl,msg));
|
||||
m_actions.push(action(MESSAGE,msg));
|
||||
lock.unlock();
|
||||
m_action_cond.notify_one();
|
||||
}
|
||||
@@ -112,13 +111,13 @@ public:
|
||||
lock.unlock();
|
||||
|
||||
if (a.type == SUBSCRIBE) {
|
||||
unique_lock<mutex> con_lock(m_connection_lock);
|
||||
unique_lock<mutex> lock(m_connection_lock);
|
||||
m_connections.insert(a.hdl);
|
||||
} else if (a.type == UNSUBSCRIBE) {
|
||||
unique_lock<mutex> con_lock(m_connection_lock);
|
||||
unique_lock<mutex> lock(m_connection_lock);
|
||||
m_connections.erase(a.hdl);
|
||||
} else if (a.type == MESSAGE) {
|
||||
unique_lock<mutex> con_lock(m_connection_lock);
|
||||
unique_lock<mutex> lock(m_connection_lock);
|
||||
|
||||
con_list::iterator it;
|
||||
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
|
||||
@@ -143,13 +142,13 @@ private:
|
||||
|
||||
int main() {
|
||||
try {
|
||||
broadcast_server server_instance;
|
||||
broadcast_server server;
|
||||
|
||||
// Start a thread to run the processing loop
|
||||
thread t(bind(&broadcast_server::process_messages,&server_instance));
|
||||
thread t(bind(&broadcast_server::process_messages,&server));
|
||||
|
||||
// Run the asio loop with the main thread
|
||||
server_instance.run(9002);
|
||||
server.run(9002);
|
||||
|
||||
t.join();
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
## Debug client example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
Import('tls_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is available build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env_cpp11.Program('debug_client', ["debug_client.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env.Program('debug_client', ["debug_client.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
*/
|
||||
|
||||
/** ====== WARNING ========
|
||||
* This example is presently used as a scratch space. It may or may not be broken
|
||||
* at any given time.
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/asio_client.hpp>
|
||||
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
|
||||
|
||||
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 websocketpp::config::asio_tls_client::message_type::ptr message_ptr;
|
||||
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
|
||||
typedef client::connection_ptr connection_ptr;
|
||||
|
||||
|
||||
|
||||
class perftest {
|
||||
public:
|
||||
typedef perftest type;
|
||||
typedef std::chrono::duration<int,std::micro> dur_type;
|
||||
|
||||
perftest () {
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
// Initialize ASIO
|
||||
m_endpoint.init_asio();
|
||||
|
||||
// Register our handlers
|
||||
m_endpoint.set_socket_init_handler(bind(&type::on_socket_init,this,::_1));
|
||||
m_endpoint.set_tls_init_handler(bind(&type::on_tls_init,this,::_1));
|
||||
m_endpoint.set_message_handler(bind(&type::on_message,this,::_1,::_2));
|
||||
m_endpoint.set_open_handler(bind(&type::on_open,this,::_1));
|
||||
m_endpoint.set_close_handler(bind(&type::on_close,this,::_1));
|
||||
}
|
||||
|
||||
void start(std::string uri) {
|
||||
websocketpp::lib::error_code ec;
|
||||
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
|
||||
|
||||
if (ec) {
|
||||
m_endpoint.get_alog().write(websocketpp::log::alevel::app,ec.message());
|
||||
}
|
||||
|
||||
//con->set_proxy("http://humupdates.uchicago.edu:8443");
|
||||
|
||||
m_endpoint.connect(con);
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
m_start = std::chrono::high_resolution_clock::now();
|
||||
m_endpoint.run();
|
||||
}
|
||||
|
||||
void on_socket_init(websocketpp::connection_hdl hdl) {
|
||||
m_socket_init = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
context_ptr on_tls_init(websocketpp::connection_hdl hdl) {
|
||||
m_tls_init = std::chrono::high_resolution_clock::now();
|
||||
context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1));
|
||||
|
||||
try {
|
||||
ctx->set_options(boost::asio::ssl::context::default_workarounds |
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
} catch (std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void on_open(websocketpp::connection_hdl hdl) {
|
||||
m_open = std::chrono::high_resolution_clock::now();
|
||||
m_endpoint.send(hdl, "", websocketpp::frame::opcode::text);
|
||||
}
|
||||
void on_message(websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
m_message = std::chrono::high_resolution_clock::now();
|
||||
m_endpoint.close(hdl,websocketpp::close::status::going_away,"");
|
||||
}
|
||||
void on_close(websocketpp::connection_hdl hdl) {
|
||||
m_close = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::cout << "Socket Init: " << std::chrono::duration_cast<dur_type>(m_socket_init-m_start).count() << std::endl;
|
||||
std::cout << "TLS Init: " << std::chrono::duration_cast<dur_type>(m_tls_init-m_start).count() << std::endl;
|
||||
std::cout << "Open: " << std::chrono::duration_cast<dur_type>(m_open-m_start).count() << std::endl;
|
||||
std::cout << "Message: " << std::chrono::duration_cast<dur_type>(m_message-m_start).count() << std::endl;
|
||||
std::cout << "Close: " << std::chrono::duration_cast<dur_type>(m_close-m_start).count() << std::endl;
|
||||
}
|
||||
private:
|
||||
client m_endpoint;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point m_start;
|
||||
std::chrono::high_resolution_clock::time_point m_socket_init;
|
||||
std::chrono::high_resolution_clock::time_point m_tls_init;
|
||||
std::chrono::high_resolution_clock::time_point m_open;
|
||||
std::chrono::high_resolution_clock::time_point m_message;
|
||||
std::chrono::high_resolution_clock::time_point m_close;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string uri = "wss://echo.websocket.org";
|
||||
|
||||
if (argc == 2) {
|
||||
uri = argv[1];
|
||||
}
|
||||
|
||||
try {
|
||||
perftest endpoint;
|
||||
endpoint.start(uri);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
file (GLOB SOURCE_FILES *.cpp)
|
||||
file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
init_target (debug_server)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
@@ -1,23 +0,0 @@
|
||||
## Debug 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 available 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('debug_server', ["debug_server.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('debug_server', ["debug_server.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
*/
|
||||
|
||||
/** ====== WARNING ========
|
||||
* This example is presently used as a scratch space. It may or may not be broken
|
||||
* at any given time.
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/debug_asio_no_tls.hpp>
|
||||
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::debug_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) {
|
||||
std::cout << "on_message called with hdl: " << hdl.lock().get()
|
||||
<< " and message: " << msg->get_payload()
|
||||
<< std::endl;
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
std::cout << "Echo failed because: " << e
|
||||
<< "(" << e.message() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Create a server endpoint
|
||||
server echo_server;
|
||||
|
||||
try {
|
||||
// Set logging settings
|
||||
echo_server.set_access_channels(websocketpp::log::alevel::all);
|
||||
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
|
||||
|
||||
// Initialize ASIO
|
||||
echo_server.init_asio();
|
||||
echo_server.set_reuse_addr(true);
|
||||
|
||||
// Register our message handler
|
||||
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
|
||||
|
||||
// Listen on port 9012
|
||||
echo_server.listen(9012);
|
||||
|
||||
// Start the server accept loop
|
||||
echo_server.start_accept();
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
echo_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;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
Import('tls_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
@@ -14,10 +15,10 @@ prgs = []
|
||||
|
||||
# if a C++11 environment is available build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env_cpp11.Program('testee_server', ["testee_server.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
|
||||
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env.Program('testee_server', ["testee_server.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -25,10 +25,37 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
//#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/config/asio.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
#include <websocketpp/message_buffer/fixed.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
|
||||
|
||||
std::string get_password() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
context_ptr on_tls_init(websocketpp::connection_hdl hdl) {
|
||||
//std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl;
|
||||
context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1));
|
||||
|
||||
try {
|
||||
ctx->set_options(boost::asio::ssl::context::default_workarounds |
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
ctx->set_password_callback(bind(&get_password));
|
||||
ctx->use_certificate_chain_file("server.pem");
|
||||
ctx->use_private_key_file("server.pem", boost::asio::ssl::context::pem);
|
||||
} catch (std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
struct testee_config : public websocketpp::config::asio {
|
||||
// pull default settings from our core config
|
||||
typedef websocketpp::config::asio core;
|
||||
@@ -40,6 +67,11 @@ struct testee_config : public websocketpp::config::asio {
|
||||
typedef core::con_msg_manager_type con_msg_manager_type;
|
||||
typedef core::endpoint_msg_manager_type endpoint_msg_manager_type;
|
||||
|
||||
//typedef websocketpp::message_buffer::fixed::policy::message message_type;
|
||||
//typedef websocketpp::message_buffer::fixed::policy::con_msg_manager con_msg_manager_type;
|
||||
////typedef websocketpp::message_buffer::fixed::policy::con_msg_manager endpoint_msg_manager_type;
|
||||
//typedef websocketpp::message_buffer::fixed::policy::endpoint_msg_manager endpoint_msg_manager_type;
|
||||
|
||||
typedef core::alog_type alog_type;
|
||||
typedef core::elog_type elog_type;
|
||||
typedef core::rng_type rng_type;
|
||||
@@ -80,13 +112,8 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}
|
||||
|
||||
void on_socket_init(websocketpp::connection_hdl hdl, boost::asio::ip::tcp::socket & s) {
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
s.set_option(option);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
// Create a server endpoint
|
||||
// Create a server endpoint
|
||||
server testee_server;
|
||||
|
||||
short port = 9002;
|
||||
@@ -97,27 +124,26 @@ int main(int argc, char * argv[]) {
|
||||
num_threads = atoi(argv[2]);
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
// Total silence
|
||||
testee_server.clear_access_channels(websocketpp::log::alevel::all);
|
||||
testee_server.clear_error_channels(websocketpp::log::alevel::all);
|
||||
//testee_server.clear_access_channels(websocketpp::log::alevel::all);
|
||||
//testee_server.clear_error_channels(websocketpp::log::alevel::all);
|
||||
|
||||
// Initialize ASIO
|
||||
testee_server.init_asio();
|
||||
testee_server.set_reuse_addr(true);
|
||||
|
||||
// Register our message handler
|
||||
testee_server.set_message_handler(bind(&on_message,&testee_server,::_1,::_2));
|
||||
testee_server.set_socket_init_handler(bind(&on_socket_init,::_1,::_2));
|
||||
//testee_server.set_tls_init_handler(bind(&on_tls_init,::_1));
|
||||
|
||||
// Listen on specified port with extended listen backlog
|
||||
testee_server.set_listen_backlog(8192);
|
||||
|
||||
// Listen on port
|
||||
testee_server.listen(port);
|
||||
|
||||
// Start the server accept loop
|
||||
testee_server.start_accept();
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
// Start the ASIO io_service run loop
|
||||
if (num_threads == 1) {
|
||||
testee_server.run();
|
||||
} else {
|
||||
@@ -131,7 +157,6 @@ int main(int argc, char * argv[]) {
|
||||
ts[i]->join();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::exception & e) {
|
||||
std::cout << "exception: " << e.what() << std::endl;
|
||||
} catch (websocketpp::lib::error_code e) {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
file (GLOB SOURCE_FILES *.cpp)
|
||||
file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
init_target (utility_client)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
|
||||
@@ -6,6 +6,7 @@ Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
Import('tls_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
@@ -14,10 +15,10 @@ prgs = []
|
||||
|
||||
# if a C++11 environment is available build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env_cpp11.Program('utility_client', ["utility_client.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs]
|
||||
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env.Program('utility_client', ["utility_client.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -1,270 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
/**
|
||||
* This example is presently used as a scratch space. It may or may not be broken
|
||||
* at any given time.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility client tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_client
|
||||
// directory of the WebSocket++ repository.
|
||||
#include <websocketpp/config/asio_client.hpp>
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
|
||||
|
||||
class connection_metadata {
|
||||
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 websocketpp::config::asio_tls_client::message_type::ptr message_ptr;
|
||||
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
|
||||
typedef client::connection_ptr connection_ptr;
|
||||
|
||||
|
||||
|
||||
class perftest {
|
||||
public:
|
||||
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
|
||||
typedef perftest type;
|
||||
typedef std::chrono::duration<int,std::micro> dur_type;
|
||||
|
||||
connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
|
||||
: m_id(id)
|
||||
, m_hdl(hdl)
|
||||
, m_status("Connecting")
|
||||
, m_uri(uri)
|
||||
, m_server("N/A")
|
||||
{}
|
||||
|
||||
void on_open(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Open";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
}
|
||||
|
||||
void on_fail(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Failed";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
m_error_reason = con->get_ec().message();
|
||||
}
|
||||
|
||||
void on_close(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Closed";
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
std::stringstream s;
|
||||
s << "close code: " << con->get_remote_close_code() << " ("
|
||||
<< websocketpp::close::status::get_string(con->get_remote_close_code())
|
||||
<< "), close reason: " << con->get_remote_close_reason();
|
||||
m_error_reason = s.str();
|
||||
}
|
||||
|
||||
websocketpp::connection_hdl get_hdl() const {
|
||||
return m_hdl;
|
||||
}
|
||||
|
||||
int get_id() const {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
std::string get_status() const {
|
||||
return m_status;
|
||||
}
|
||||
|
||||
friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
|
||||
private:
|
||||
int m_id;
|
||||
websocketpp::connection_hdl m_hdl;
|
||||
std::string m_status;
|
||||
std::string m_uri;
|
||||
std::string m_server;
|
||||
std::string m_error_reason;
|
||||
};
|
||||
|
||||
std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
|
||||
out << "> URI: " << data.m_uri << "\n"
|
||||
<< "> Status: " << data.m_status << "\n"
|
||||
<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
|
||||
<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
class websocket_endpoint {
|
||||
public:
|
||||
websocket_endpoint () : m_next_id(0) {
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
perftest () {
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::none);
|
||||
m_endpoint.set_error_channels(websocketpp::log::elevel::none);
|
||||
|
||||
// Initialize ASIO
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
// Register our handlers
|
||||
m_endpoint.set_tls_init_handler(bind(&type::on_tls_init,this,::_1));
|
||||
|
||||
m_endpoint.set_tcp_pre_init_handler(bind(&type::on_tcp_pre_init,this,::_1));
|
||||
m_endpoint.set_tcp_post_init_handler(bind(&type::on_tcp_post_init,this,::_1));
|
||||
m_endpoint.set_socket_init_handler(bind(&type::on_socket_init,this,::_1));
|
||||
m_endpoint.set_message_handler(bind(&type::on_message,this,::_1,::_2));
|
||||
m_endpoint.set_open_handler(bind(&type::on_open,this,::_1));
|
||||
m_endpoint.set_close_handler(bind(&type::on_close,this,::_1));
|
||||
}
|
||||
|
||||
~websocket_endpoint() {
|
||||
m_endpoint.stop_perpetual();
|
||||
|
||||
for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
|
||||
if (it->second->get_status() != "Open") {
|
||||
// Only close open connections
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "> Closing connection " << it->second->get_id() << std::endl;
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
|
||||
if (ec) {
|
||||
std::cout << "> Error closing connection " << it->second->get_id() << ": "
|
||||
<< ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
int connect(std::string const & uri) {
|
||||
void start(std::string uri) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
|
||||
|
||||
if (ec) {
|
||||
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
|
||||
return -1;
|
||||
m_endpoint.get_alog().write(websocketpp::log::alevel::app,ec.message());
|
||||
}
|
||||
|
||||
int new_id = m_next_id++;
|
||||
connection_metadata::ptr metadata_ptr(new connection_metadata(new_id, con->get_handle(), uri));
|
||||
m_connection_list[new_id] = metadata_ptr;
|
||||
|
||||
con->set_open_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_open,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
con->set_fail_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_fail,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
con->set_close_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_close,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
//con->set_proxy("http://humupdates.uchicago.edu:8443");
|
||||
|
||||
m_endpoint.connect(con);
|
||||
|
||||
return new_id;
|
||||
// Start the ASIO io_service run loop
|
||||
m_start = std::chrono::high_resolution_clock::now();
|
||||
m_endpoint.run();
|
||||
}
|
||||
|
||||
void close(int id, websocketpp::close::status::value code, std::string reason) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
con_list::iterator metadata_it = m_connection_list.find(id);
|
||||
if (metadata_it == m_connection_list.end()) {
|
||||
std::cout << "> No connection found with id " << id << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_endpoint.close(metadata_it->second->get_hdl(), code, reason, ec);
|
||||
if (ec) {
|
||||
std::cout << "> Error initiating close: " << ec.message() << std::endl;
|
||||
}
|
||||
void on_tcp_pre_init(websocketpp::connection_hdl hdl) {
|
||||
m_tcp_pre_init = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
void on_tcp_post_init(websocketpp::connection_hdl hdl) {
|
||||
m_tcp_post_init = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
void on_socket_init(websocketpp::connection_hdl hdl) {
|
||||
m_socket_init = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
connection_metadata::ptr get_metadata(int id) const {
|
||||
con_list::const_iterator metadata_it = m_connection_list.find(id);
|
||||
if (metadata_it == m_connection_list.end()) {
|
||||
return connection_metadata::ptr();
|
||||
context_ptr on_tls_init(websocketpp::connection_hdl hdl) {
|
||||
context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1));
|
||||
|
||||
try {
|
||||
ctx->set_options(boost::asio::ssl::context::default_workarounds |
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
} catch (std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void on_open(websocketpp::connection_hdl hdl) {
|
||||
m_open = std::chrono::high_resolution_clock::now();
|
||||
|
||||
client::connection_ptr con = m_endpoint.get_con_from_hdl(hdl);
|
||||
|
||||
m_msg = con->get_message(websocketpp::frame::opcode::text,64);
|
||||
m_msg->append_payload(std::string(60,'*'));
|
||||
m_msg_count = 1;
|
||||
|
||||
//m_message_stamps.reserve(1000);
|
||||
|
||||
m_con_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
m_endpoint.send(hdl, m_msg);
|
||||
//m_endpoint.send(hdl, "", websocketpp::frame::opcode::text);
|
||||
}
|
||||
void on_message(websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
if (m_msg_count == 1000) {
|
||||
m_message = std::chrono::high_resolution_clock::now();
|
||||
m_endpoint.close(hdl,websocketpp::close::status::going_away,"");
|
||||
} else {
|
||||
return metadata_it->second;
|
||||
m_msg_count++;
|
||||
m_endpoint.send(hdl, m_msg);
|
||||
}
|
||||
}
|
||||
void on_close(websocketpp::connection_hdl hdl) {
|
||||
m_close = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
||||
|
||||
std::cout << "Socket Init: " << std::chrono::duration_cast<dur_type>(m_socket_init-m_start).count() << std::endl;
|
||||
std::cout << "TCP Pre Init: " << std::chrono::duration_cast<dur_type>(m_tcp_pre_init-m_start).count() << std::endl;
|
||||
std::cout << "TCP Post Init: " << std::chrono::duration_cast<dur_type>(m_tcp_post_init-m_start).count() << std::endl;
|
||||
std::cout << "Open: " << std::chrono::duration_cast<dur_type>(m_open-m_start).count() << std::endl;
|
||||
std::cout << "Start: " << std::chrono::duration_cast<dur_type>(m_con_start-m_start).count() << std::endl;
|
||||
std::cout << "Message: " << std::chrono::duration_cast<dur_type>(m_message-m_start).count() << std::endl;
|
||||
std::cout << "Close: " << std::chrono::duration_cast<dur_type>(m_close-m_start).count() << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Message: " << std::chrono::duration_cast<dur_type>(m_message-m_con_start).count() << std::endl;
|
||||
std::cout << "Close: " << std::chrono::duration_cast<dur_type>(m_close-m_message).count() << std::endl;
|
||||
}
|
||||
private:
|
||||
typedef std::map<int,connection_metadata::ptr> con_list;
|
||||
|
||||
client m_endpoint;
|
||||
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
|
||||
|
||||
con_list m_connection_list;
|
||||
int m_next_id;
|
||||
client::message_ptr m_msg;
|
||||
size_t m_msg_count;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point m_start;
|
||||
std::chrono::high_resolution_clock::time_point m_tcp_pre_init;
|
||||
std::chrono::high_resolution_clock::time_point m_tcp_post_init;
|
||||
std::chrono::high_resolution_clock::time_point m_socket_init;
|
||||
|
||||
std::vector<std::chrono::high_resolution_clock::time_point> m_message_stamps;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point m_con_start;
|
||||
std::chrono::high_resolution_clock::time_point m_open;
|
||||
std::chrono::high_resolution_clock::time_point m_message;
|
||||
std::chrono::high_resolution_clock::time_point m_close;
|
||||
};
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
websocket_endpoint endpoint;
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string uri = "wss://echo.websocket.org";
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
if (argc == 2) {
|
||||
uri = argv[1];
|
||||
}
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "connect <ws uri>\n"
|
||||
<< "close <connection id> [<close code:default=1000>] [<close reason>]\n"
|
||||
<< "show <connection id>\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else if (input.substr(0,7) == "connect") {
|
||||
int id = endpoint.connect(input.substr(8));
|
||||
if (id != -1) {
|
||||
std::cout << "> Created connection with id " << id << std::endl;
|
||||
}
|
||||
} else if (input.substr(0,5) == "close") {
|
||||
std::stringstream ss(input);
|
||||
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
endpoint.close(id, close_code, reason);
|
||||
} else if (input.substr(0,4) == "show") {
|
||||
int id = atoi(input.substr(5).c_str());
|
||||
|
||||
connection_metadata::ptr metadata = endpoint.get_metadata(id);
|
||||
if (metadata) {
|
||||
std::cout << *metadata << std::endl;
|
||||
} else {
|
||||
std::cout << "> Unknown connection id " << id << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "> Unrecognized Command" << std::endl;
|
||||
}
|
||||
try {
|
||||
perftest endpoint;
|
||||
endpoint.start(uri);
|
||||
} 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+1
-1
@@ -17,7 +17,7 @@ Complete & Tested:
|
||||
- close_handler
|
||||
- echo_server & echo_server_tls
|
||||
|
||||
Implemented, needs more testing
|
||||
Implimented, needs more testing
|
||||
- TLS support
|
||||
- External io_service support
|
||||
- socket_init_handler
|
||||
|
||||
@@ -80,7 +80,8 @@ struct stub_config : public websocketpp::config::core {
|
||||
};
|
||||
|
||||
struct connection_setup {
|
||||
connection_setup(bool p_is_server) : c(p_is_server, "", alog, elog, rng) {}
|
||||
connection_setup(bool server)
|
||||
: c(server,"",alog,elog,rng) {}
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
stub_config::alog_type alog;
|
||||
@@ -192,33 +193,6 @@ BOOST_AUTO_TEST_CASE( basic_client_websocket ) {
|
||||
BOOST_CHECK_EQUAL(ref, output.str());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( set_max_message_size ) {
|
||||
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\n\r\n";
|
||||
|
||||
// After the handshake, add a single frame with a message that is too long.
|
||||
char frame0[10] = {char(0x82), char(0x83), 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01};
|
||||
input.append(frame0, 10);
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: foo\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
// After the handshake, add a single frame with a close message with message too big
|
||||
// error code.
|
||||
char frame1[4] = {char(0x88), 0x19, 0x03, char(0xf1)};
|
||||
output.append(frame1, 4);
|
||||
output.append("A message was too large");
|
||||
|
||||
server s;
|
||||
s.set_user_agent("");
|
||||
s.set_validate_handler(bind(&validate_set_ua,&s,::_1));
|
||||
s.set_max_message_size(2);
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
}
|
||||
|
||||
// TODO: set max message size in client endpoint test case
|
||||
// TODO: set max message size mid connection test case
|
||||
// TODO: [maybe] set max message size in open handler
|
||||
|
||||
/*
|
||||
|
||||
BOOST_AUTO_TEST_CASE( user_reject_origin ) {
|
||||
|
||||
+56
-103
@@ -355,26 +355,6 @@ BOOST_AUTO_TEST_CASE( extract_parameters ) {
|
||||
BOOST_CHECK_EQUAL( a.find("bar")->second, "a \"b\" c" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( strip_lws ) {
|
||||
std::string test1 = "foo";
|
||||
std::string test2 = " foo ";
|
||||
std::string test3 = "foo ";
|
||||
std::string test4 = " foo";
|
||||
std::string test5 = " foo ";
|
||||
std::string test6 = " \r\n foo ";
|
||||
std::string test7 = " \t foo ";
|
||||
std::string test8 = " \t ";
|
||||
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test1), "foo" );
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test2), "foo" );
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test3), "foo" );
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test4), "foo" );
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test5), "foo" );
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test6), "foo" );
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test7), "foo" );
|
||||
BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test8), "" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( case_insensitive_headers ) {
|
||||
websocketpp::http::parser::parser r;
|
||||
|
||||
@@ -675,12 +655,12 @@ BOOST_AUTO_TEST_CASE( old_http_version ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 41 );
|
||||
BOOST_CHECK( pos == 41 );
|
||||
BOOST_CHECK( r.ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.0" );
|
||||
BOOST_CHECK_EQUAL( r.get_method(), "GET" );
|
||||
BOOST_CHECK_EQUAL( r.get_uri(), "/" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" );
|
||||
BOOST_CHECK( r.get_version() == "HTTP/1.0" );
|
||||
BOOST_CHECK( r.get_method() == "GET" );
|
||||
BOOST_CHECK( r.get_uri() == "/" );
|
||||
BOOST_CHECK( r.get_header("Host") == "www.example.com" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( new_http_version1 ) {
|
||||
@@ -698,12 +678,12 @@ BOOST_AUTO_TEST_CASE( new_http_version1 ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 42 );
|
||||
BOOST_CHECK( pos == 42 );
|
||||
BOOST_CHECK( r.ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.12" );
|
||||
BOOST_CHECK_EQUAL( r.get_method(), "GET" );
|
||||
BOOST_CHECK_EQUAL( r.get_uri(), "/" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" );
|
||||
BOOST_CHECK( r.get_version() == "HTTP/1.12" );
|
||||
BOOST_CHECK( r.get_method() == "GET" );
|
||||
BOOST_CHECK( r.get_uri() == "/" );
|
||||
BOOST_CHECK( r.get_header("Host") == "www.example.com" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( new_http_version2 ) {
|
||||
@@ -721,12 +701,12 @@ BOOST_AUTO_TEST_CASE( new_http_version2 ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 43 );
|
||||
BOOST_CHECK( pos == 43 );
|
||||
BOOST_CHECK( r.ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/12.12" );
|
||||
BOOST_CHECK_EQUAL( r.get_method(), "GET" );
|
||||
BOOST_CHECK_EQUAL( r.get_uri(), "/" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" );
|
||||
BOOST_CHECK( r.get_version() == "HTTP/12.12" );
|
||||
BOOST_CHECK( r.get_method() == "GET" );
|
||||
BOOST_CHECK( r.get_uri() == "/" );
|
||||
BOOST_CHECK( r.get_header("Host") == "www.example.com" );
|
||||
}
|
||||
|
||||
/* commented out due to not being implemented yet
|
||||
@@ -746,7 +726,7 @@ BOOST_AUTO_TEST_CASE( new_http_version3 ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == true );
|
||||
}*/
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( header_whitespace1 ) {
|
||||
websocketpp::http::parser::request r;
|
||||
@@ -763,12 +743,12 @@ BOOST_AUTO_TEST_CASE( header_whitespace1 ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 43 );
|
||||
BOOST_CHECK( pos == 43 );
|
||||
BOOST_CHECK( r.ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" );
|
||||
BOOST_CHECK_EQUAL( r.get_method(), "GET" );
|
||||
BOOST_CHECK_EQUAL( r.get_uri(), "/" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" );
|
||||
BOOST_CHECK( r.get_version() == "HTTP/1.1" );
|
||||
BOOST_CHECK( r.get_method() == "GET" );
|
||||
BOOST_CHECK( r.get_uri() == "/" );
|
||||
BOOST_CHECK( r.get_header("Host") == "www.example.com" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( header_whitespace2 ) {
|
||||
@@ -786,13 +766,13 @@ BOOST_AUTO_TEST_CASE( header_whitespace2 ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 40 );
|
||||
BOOST_CHECK( pos == 40 );
|
||||
BOOST_CHECK( r.ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" );
|
||||
BOOST_CHECK_EQUAL( r.get_method(), "GET" );
|
||||
BOOST_CHECK_EQUAL( r.get_uri(), "/" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" );
|
||||
}
|
||||
BOOST_CHECK( r.get_version() == "HTTP/1.1" );
|
||||
BOOST_CHECK( r.get_method() == "GET" );
|
||||
BOOST_CHECK( r.get_uri() == "/" );
|
||||
BOOST_CHECK( r.get_header("Host") == "www.example.com" );
|
||||
}*/
|
||||
|
||||
BOOST_AUTO_TEST_CASE( header_aggregation ) {
|
||||
websocketpp::http::parser::request r;
|
||||
@@ -809,12 +789,12 @@ BOOST_AUTO_TEST_CASE( header_aggregation ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 61 );
|
||||
BOOST_CHECK( pos == 61 );
|
||||
BOOST_CHECK( r.ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" );
|
||||
BOOST_CHECK_EQUAL( r.get_method(), "GET" );
|
||||
BOOST_CHECK_EQUAL( r.get_uri(), "/" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Foo"), "bar, bat" );
|
||||
BOOST_CHECK( r.get_version() == "HTTP/1.1" );
|
||||
BOOST_CHECK( r.get_method() == "GET" );
|
||||
BOOST_CHECK( r.get_uri() == "/" );
|
||||
BOOST_CHECK( r.get_header("Foo") == "bar, bat" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( wikipedia_example_response ) {
|
||||
@@ -833,42 +813,15 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 159 );
|
||||
BOOST_CHECK( pos == 159 );
|
||||
BOOST_CHECK( r.headers_ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" );
|
||||
BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols );
|
||||
BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Upgrade"), "websocket" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Connection"), "Upgrade" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Accept"), "HSmrc0sMlYUkAGmm5OPpG2HaGWk=" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Protocol"), "chat" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( response_with_non_standard_lws ) {
|
||||
websocketpp::http::parser::response r;
|
||||
|
||||
std::string raw = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept:HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\nSec-WebSocket-Protocol: chat\r\n\r\n";
|
||||
|
||||
bool exception = false;
|
||||
size_t pos = 0;
|
||||
|
||||
try {
|
||||
pos += r.consume(raw.c_str(),raw.size());
|
||||
} catch (std::exception &e) {
|
||||
exception = true;
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 158 );
|
||||
BOOST_CHECK( r.headers_ready() );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" );
|
||||
BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols );
|
||||
BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Upgrade"), "websocket" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Connection"), "Upgrade" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Accept"), "HSmrc0sMlYUkAGmm5OPpG2HaGWk=" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Protocol"), "chat" );
|
||||
BOOST_CHECK( r.get_version() == "HTTP/1.1" );
|
||||
BOOST_CHECK( r.get_status_code() == websocketpp::http::status_code::switching_protocols );
|
||||
BOOST_CHECK( r.get_status_msg() == "Switching Protocols" );
|
||||
BOOST_CHECK( r.get_header("Upgrade") == "websocket" );
|
||||
BOOST_CHECK( r.get_header("Connection") == "Upgrade" );
|
||||
BOOST_CHECK( r.get_header("Sec-WebSocket-Accept") == "HSmrc0sMlYUkAGmm5OPpG2HaGWk=" );
|
||||
BOOST_CHECK( r.get_header("Sec-WebSocket-Protocol") == "chat" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( plain_http_response ) {
|
||||
@@ -887,21 +840,21 @@ BOOST_AUTO_TEST_CASE( plain_http_response ) {
|
||||
}
|
||||
|
||||
BOOST_CHECK( exception == false );
|
||||
BOOST_CHECK_EQUAL( pos, 405 );
|
||||
BOOST_CHECK( pos == 405 );
|
||||
BOOST_CHECK( r.headers_ready() == true );
|
||||
BOOST_CHECK( r.ready() == true );
|
||||
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" );
|
||||
BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::ok );
|
||||
BOOST_CHECK_EQUAL( r.get_status_msg(), "OK" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Date"), "Thu, 10 May 2012 11:59:25 GMT" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Server"), "Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.8 with Suhosin-Patch" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Last-Modified"), "Tue, 30 Mar 2010 17:41:28 GMT" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("ETag"), "\"16799d-55-4830823a78200\"" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Accept-Ranges"), "bytes" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Content-Length"), "85" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Vary"), "Accept-Encoding" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Content-Type"), "text/html" );
|
||||
BOOST_CHECK_EQUAL( r.get_body(), "<!doctype html>\n<html>\n<head>\n<title>Thor</title>\n</head>\n<body> \n<p>Thor</p>\n</body>" );
|
||||
BOOST_CHECK( r.get_version() == "HTTP/1.1" );
|
||||
BOOST_CHECK( r.get_status_code() == websocketpp::http::status_code::ok );
|
||||
BOOST_CHECK( r.get_status_msg() == "OK" );
|
||||
BOOST_CHECK( r.get_header("Date") == "Thu, 10 May 2012 11:59:25 GMT" );
|
||||
BOOST_CHECK( r.get_header("Server") == "Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.8 with Suhosin-Patch" );
|
||||
BOOST_CHECK( r.get_header("Last-Modified") == "Tue, 30 Mar 2010 17:41:28 GMT" );
|
||||
BOOST_CHECK( r.get_header("ETag") == "\"16799d-55-4830823a78200\"" );
|
||||
BOOST_CHECK( r.get_header("Accept-Ranges") == "bytes" );
|
||||
BOOST_CHECK( r.get_header("Content-Length") == "85" );
|
||||
BOOST_CHECK( r.get_header("Vary") == "Accept-Encoding" );
|
||||
BOOST_CHECK( r.get_header("Content-Type") == "text/html" );
|
||||
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 ) {
|
||||
@@ -936,7 +889,7 @@ BOOST_AUTO_TEST_CASE( write_request_basic ) {
|
||||
r.set_method("GET");
|
||||
r.set_uri("/");
|
||||
|
||||
BOOST_CHECK_EQUAL( r.raw(), raw );
|
||||
BOOST_CHECK( r.raw() == raw );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( write_request_with_header ) {
|
||||
@@ -949,7 +902,7 @@ BOOST_AUTO_TEST_CASE( write_request_with_header ) {
|
||||
r.set_uri("/");
|
||||
r.replace_header("Host","http://example.com");
|
||||
|
||||
BOOST_CHECK_EQUAL( r.raw(), raw );
|
||||
BOOST_CHECK( r.raw() == raw );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( write_request_with_body ) {
|
||||
@@ -964,5 +917,5 @@ BOOST_AUTO_TEST_CASE( write_request_with_body ) {
|
||||
r.replace_header("Content-Type","application/x-www-form-urlencoded");
|
||||
r.set_body("licenseID=string&content=string¶msXML=string");
|
||||
|
||||
BOOST_CHECK_EQUAL( r.raw(), raw );
|
||||
BOOST_CHECK( r.raw() == raw );
|
||||
}
|
||||
@@ -36,7 +36,6 @@
|
||||
#include <websocketpp/http/response.hpp>
|
||||
#include <websocketpp/message_buffer/message.hpp>
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
#include <websocketpp/logger/stub.hpp>
|
||||
|
||||
struct stub_config {
|
||||
typedef websocketpp::http::parser::request request_type;
|
||||
@@ -46,13 +45,6 @@ struct stub_config {
|
||||
<websocketpp::message_buffer::alloc::con_msg_manager> message_type;
|
||||
typedef websocketpp::message_buffer::alloc::con_msg_manager<message_type>
|
||||
con_msg_manager_type;
|
||||
|
||||
typedef websocketpp::log::stub alog_type;
|
||||
typedef websocketpp::log::stub elog_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
|
||||
|
||||
};
|
||||
|
||||
struct processor_setup {
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
#include <websocketpp/random/none.hpp>
|
||||
#include <websocketpp/logger/stub.hpp>
|
||||
|
||||
struct stub_config {
|
||||
typedef websocketpp::http::parser::request request_type;
|
||||
@@ -49,13 +48,8 @@ struct stub_config {
|
||||
typedef websocketpp::message_buffer::alloc::con_msg_manager<message_type>
|
||||
con_msg_manager_type;
|
||||
|
||||
typedef websocketpp::log::stub alog_type;
|
||||
typedef websocketpp::log::stub elog_type;
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
|
||||
/// Extension related config
|
||||
static const bool enable_extensions = false;
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
#include <websocketpp/random/none.hpp>
|
||||
#include <websocketpp/logger/stub.hpp>
|
||||
|
||||
struct stub_config {
|
||||
typedef websocketpp::http::parser::request request_type;
|
||||
@@ -49,13 +48,8 @@ struct stub_config {
|
||||
typedef websocketpp::message_buffer::alloc::con_msg_manager<message_type>
|
||||
con_msg_manager_type;
|
||||
|
||||
typedef websocketpp::log::stub alog_type;
|
||||
typedef websocketpp::log::stub elog_type;
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
|
||||
/// Extension related config
|
||||
static const bool enable_extensions = false;
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <websocketpp/message_buffer/message.hpp>
|
||||
#include <websocketpp/message_buffer/alloc.hpp>
|
||||
#include <websocketpp/random/none.hpp>
|
||||
#include <websocketpp/logger/stub.hpp>
|
||||
|
||||
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
|
||||
@@ -52,9 +51,6 @@ struct stub_config {
|
||||
typedef websocketpp::message_buffer::alloc::con_msg_manager<message_type>
|
||||
con_msg_manager_type;
|
||||
|
||||
typedef websocketpp::log::stub alog_type;
|
||||
typedef websocketpp::log::stub elog_type;
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
struct permessage_deflate_config {
|
||||
@@ -64,7 +60,6 @@ struct stub_config {
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
static const bool enable_extensions = false;
|
||||
};
|
||||
|
||||
@@ -77,9 +72,6 @@ struct stub_config_ext {
|
||||
typedef websocketpp::message_buffer::alloc::con_msg_manager<message_type>
|
||||
con_msg_manager_type;
|
||||
|
||||
typedef websocketpp::log::stub alog_type;
|
||||
typedef websocketpp::log::stub elog_type;
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
struct permessage_deflate_config {
|
||||
@@ -89,7 +81,6 @@ struct stub_config_ext {
|
||||
typedef websocketpp::extensions::permessage_deflate::enabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
static const bool enable_extensions = true;
|
||||
};
|
||||
|
||||
@@ -498,36 +489,6 @@ BOOST_AUTO_TEST_CASE( prepare_data_frame ) {
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( single_frame_message_too_large ) {
|
||||
processor_setup env(true);
|
||||
|
||||
env.p.set_max_message_size(3);
|
||||
|
||||
uint8_t frame0[10] = {0x82, 0x84, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01};
|
||||
|
||||
// read message that is one byte too large
|
||||
BOOST_CHECK_EQUAL( env.p.consume(frame0,10,env.ec), 6 );
|
||||
BOOST_CHECK_EQUAL( env.ec, websocketpp::processor::error::message_too_big );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( multiple_frame_message_too_large ) {
|
||||
processor_setup env(true);
|
||||
|
||||
env.p.set_max_message_size(4);
|
||||
|
||||
uint8_t frame0[8] = {0x02, 0x82, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01};
|
||||
uint8_t frame1[9] = {0x80, 0x83, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01};
|
||||
|
||||
// read first message frame with size under the limit
|
||||
BOOST_CHECK_EQUAL( env.p.consume(frame0,8,env.ec), 8 );
|
||||
BOOST_CHECK( !env.ec );
|
||||
|
||||
// read second message frame that puts the size over the limit
|
||||
BOOST_CHECK_EQUAL( env.p.consume(frame1,9,env.ec), 6 );
|
||||
BOOST_CHECK_EQUAL( env.ec, websocketpp::processor::error::message_too_big );
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( client_handshake_request ) {
|
||||
processor_setup env(false);
|
||||
|
||||
@@ -132,4 +132,4 @@ BOOST_AUTO_TEST_CASE( version_non_numeric ) {
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == -1);
|
||||
}
|
||||
}
|
||||
@@ -30,11 +30,8 @@
|
||||
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
|
||||
#include <websocketpp/config/core.hpp>
|
||||
#include <websocketpp/config/core_client.hpp>
|
||||
#include <websocketpp/config/asio.hpp>
|
||||
#include <websocketpp/config/asio_client.hpp>
|
||||
#include <websocketpp/config/debug_asio.hpp>
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
@@ -80,57 +77,9 @@ struct config : public websocketpp::config::asio_client {
|
||||
static const long timeout_pong = 500;
|
||||
};
|
||||
|
||||
struct config_tls : public websocketpp::config::asio_tls_client {
|
||||
typedef config type;
|
||||
typedef websocketpp::config::asio 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 : public base::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;
|
||||
|
||||
//static const websocketpp::log::level elog_level = websocketpp::log::elevel::all;
|
||||
//static const websocketpp::log::level alog_level = websocketpp::log::alevel::all;
|
||||
|
||||
/// Length of time before an opening handshake is aborted
|
||||
static const long timeout_open_handshake = 500;
|
||||
/// Length of time before a closing handshake is aborted
|
||||
static const long timeout_close_handshake = 500;
|
||||
/// Length of time to wait for a pong after a ping
|
||||
static const long timeout_pong = 500;
|
||||
};
|
||||
|
||||
typedef websocketpp::server<config> server;
|
||||
typedef websocketpp::client<config> client;
|
||||
|
||||
typedef websocketpp::server<config_tls> server_tls;
|
||||
typedef websocketpp::client<config_tls> client_tls;
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::core> iostream_server;
|
||||
typedef websocketpp::client<websocketpp::config::core_client> iostream_client;
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
@@ -154,7 +103,6 @@ void run_server(server * s, int port, bool log = false) {
|
||||
}
|
||||
|
||||
s->init_asio();
|
||||
s->set_reuse_addr(true);
|
||||
|
||||
s->listen(port);
|
||||
s->start_accept();
|
||||
@@ -169,11 +117,9 @@ void run_client(client & c, std::string uri, bool log = false) {
|
||||
c.clear_access_channels(websocketpp::log::alevel::all);
|
||||
c.clear_error_channels(websocketpp::log::elevel::all);
|
||||
}
|
||||
websocketpp::lib::error_code ec;
|
||||
c.init_asio(ec);
|
||||
c.set_reuse_addr(true);
|
||||
BOOST_CHECK(!ec);
|
||||
c.init_asio();
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
client::connection_ptr con = c.get_connection(uri,ec);
|
||||
BOOST_CHECK( !ec );
|
||||
c.connect(con);
|
||||
@@ -181,14 +127,6 @@ void run_client(client & c, std::string uri, bool log = false) {
|
||||
c.run();
|
||||
}
|
||||
|
||||
void run_client_and_mark(client * c, bool * flag, websocketpp::lib::mutex * mutex) {
|
||||
c->run();
|
||||
BOOST_CHECK( true );
|
||||
websocketpp::lib::lock_guard<websocketpp::lib::mutex> lock(*mutex);
|
||||
*flag = true;
|
||||
BOOST_CHECK( true );
|
||||
}
|
||||
|
||||
void run_time_limited_client(client & c, std::string uri, long timeout,
|
||||
bool log)
|
||||
{
|
||||
@@ -456,7 +394,7 @@ BOOST_AUTO_TEST_CASE( client_self_initiated_close_handshake_timeout ) {
|
||||
websocketpp::lib::thread tthread(websocketpp::lib::bind(&run_test_timer,6));
|
||||
tthread.detach();
|
||||
|
||||
run_client(c, "http://localhost:9005", false);
|
||||
run_client(c, "http://localhost:9005",false);
|
||||
|
||||
sthread.join();
|
||||
}
|
||||
@@ -492,115 +430,3 @@ BOOST_AUTO_TEST_CASE( server_self_initiated_close_handshake_timeout ) {
|
||||
|
||||
sthread.join();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( client_runs_out_of_work ) {
|
||||
client c;
|
||||
|
||||
websocketpp::lib::thread tthread(websocketpp::lib::bind(&run_test_timer,3));
|
||||
tthread.detach();
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
c.init_asio(ec);
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
c.run();
|
||||
|
||||
// This test checks that an io_service with no work ends immediately.
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( client_is_perpetual ) {
|
||||
client c;
|
||||
bool flag = false;
|
||||
websocketpp::lib::mutex mutex;
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
c.init_asio(ec);
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
c.start_perpetual();
|
||||
|
||||
websocketpp::lib::thread cthread(websocketpp::lib::bind(&run_client_and_mark,&c,&flag,&mutex));
|
||||
|
||||
sleep(1);
|
||||
|
||||
{
|
||||
// Checks that the thread hasn't exited yet
|
||||
websocketpp::lib::lock_guard<websocketpp::lib::mutex> lock(mutex);
|
||||
BOOST_CHECK( !flag );
|
||||
}
|
||||
|
||||
c.stop_perpetual();
|
||||
|
||||
sleep(1);
|
||||
|
||||
{
|
||||
// Checks that the thread has exited
|
||||
websocketpp::lib::lock_guard<websocketpp::lib::mutex> lock(mutex);
|
||||
BOOST_CHECK( flag );
|
||||
}
|
||||
|
||||
cthread.join();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( client_failed_connection ) {
|
||||
client c;
|
||||
|
||||
run_time_limited_client(c,"http://localhost:9005", 5, false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stop_listening ) {
|
||||
server s;
|
||||
client c;
|
||||
|
||||
// the first connection stops the server from listening
|
||||
s.set_open_handler(bind(&cancel_on_open,&s,::_1));
|
||||
|
||||
// client immediately closes after opening a connection
|
||||
c.set_open_handler(bind(&close<client>,&c,::_1));
|
||||
|
||||
websocketpp::lib::thread sthread(websocketpp::lib::bind(&run_server,&s,9005,false));
|
||||
websocketpp::lib::thread tthread(websocketpp::lib::bind(&run_test_timer,2));
|
||||
tthread.detach();
|
||||
|
||||
run_client(c, "http://localhost:9005",false);
|
||||
|
||||
sthread.join();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( pause_reading ) {
|
||||
iostream_server s;
|
||||
std::string handshake = "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\n\r\n";
|
||||
char buffer[2] = { char(0x81), char(0x80) };
|
||||
|
||||
// suppress output (it needs a place to go to avoid error but we don't care what it is)
|
||||
std::stringstream null_output;
|
||||
s.register_ostream(&null_output);
|
||||
|
||||
iostream_server::connection_ptr con = s.get_connection();
|
||||
con->start();
|
||||
|
||||
// read handshake, should work
|
||||
BOOST_CHECK_EQUAL( con->read_some(handshake.data(), handshake.length()), handshake.length());
|
||||
|
||||
// pause reading and try again. The first read should work, the second should return 0
|
||||
// the first read was queued already after the handshake so it will go through because
|
||||
// reading wasn't paused when it was queued. The byte it reads wont be enough to
|
||||
// complete the frame so another read will be requested. This one wont actually happen
|
||||
// because the connection is paused now.
|
||||
con->pause_reading();
|
||||
BOOST_CHECK_EQUAL( con->read_some(buffer, 1), 1);
|
||||
BOOST_CHECK_EQUAL( con->read_some(buffer+1, 1), 0);
|
||||
// resume reading and try again. Should work this time because the resume should have
|
||||
// re-queued a read.
|
||||
con->resume_reading();
|
||||
BOOST_CHECK_EQUAL( con->read_some(buffer+1, 1), 1);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( server_connection_cleanup ) {
|
||||
server_tls s;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ struct stub_con : public iostream_con {
|
||||
typedef websocketpp::lib::shared_ptr<type> ptr;
|
||||
typedef iostream_con::timer_ptr timer_ptr;
|
||||
|
||||
stub_con(bool is_server, config::alog_type & a, config::elog_type & e)
|
||||
stub_con(bool is_server, config::alog_type &a, config::elog_type & e)
|
||||
: iostream_con(is_server,a,e)
|
||||
// Set the error to a known code that is unused by the library
|
||||
// This way we can easily confirm that the handler was run at all.
|
||||
@@ -107,7 +107,7 @@ struct stub_con : public iostream_con {
|
||||
);
|
||||
}
|
||||
|
||||
void handle_op(websocketpp::lib::error_code const & e) {
|
||||
void handle_op(const websocketpp::lib::error_code& e) {
|
||||
ec = e;
|
||||
}
|
||||
|
||||
@@ -115,18 +115,18 @@ struct stub_con : public iostream_con {
|
||||
};
|
||||
|
||||
// Stubs
|
||||
config::alog_type alogger;
|
||||
config::elog_type elogger;
|
||||
config::alog_type a;
|
||||
config::elog_type e;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( const_methods ) {
|
||||
iostream_con::ptr con(new iostream_con(true,alogger,elogger));
|
||||
iostream_con::ptr con(new iostream_con(true,a,e));
|
||||
|
||||
BOOST_CHECK( con->is_secure() == false );
|
||||
BOOST_CHECK( con->get_remote_endpoint() == "iostream transport" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( write_before_ostream_set ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
con->write("foo");
|
||||
BOOST_CHECK( con->ec == make_error_code(websocketpp::transport::iostream::error::output_stream_required) );
|
||||
@@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE( write_before_ostream_set ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( async_write ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
std::stringstream output;
|
||||
|
||||
@@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE( async_write ) {
|
||||
BOOST_AUTO_TEST_CASE( async_write_vector_0 ) {
|
||||
std::stringstream output;
|
||||
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
con->register_ostream(&output);
|
||||
|
||||
std::vector<websocketpp::transport::buffer> bufs;
|
||||
@@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE( async_write_vector_0 ) {
|
||||
BOOST_AUTO_TEST_CASE( async_write_vector_1 ) {
|
||||
std::stringstream output;
|
||||
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
con->register_ostream(&output);
|
||||
|
||||
std::vector<websocketpp::transport::buffer> bufs;
|
||||
@@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE( async_write_vector_1 ) {
|
||||
BOOST_AUTO_TEST_CASE( async_write_vector_2 ) {
|
||||
std::stringstream output;
|
||||
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
con->register_ostream(&output);
|
||||
|
||||
std::vector<websocketpp::transport::buffer> bufs;
|
||||
@@ -202,7 +202,7 @@ BOOST_AUTO_TEST_CASE( async_write_vector_2 ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( async_read_at_least_too_much ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
char buf[10];
|
||||
|
||||
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE( async_read_at_least_too_much ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( async_read_at_least_double_read ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
char buf[10];
|
||||
|
||||
@@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE( async_read_at_least_double_read ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( async_read_at_least ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
char buf[10];
|
||||
|
||||
@@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE( async_read_at_least ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( async_read_at_least2 ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
char buf[10];
|
||||
|
||||
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE( async_read_at_least2 ) {
|
||||
void timer_callback_stub(const websocketpp::lib::error_code & ec) {}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( set_timer ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
stub_con::timer_ptr tp = con->set_timer(1000,timer_callback_stub);
|
||||
|
||||
@@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE( set_timer ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( async_read_at_least_read_some ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
char buf[10];
|
||||
memset(buf,'x',10);
|
||||
@@ -315,7 +315,7 @@ BOOST_AUTO_TEST_CASE( async_read_at_least_read_some ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( eof_flag ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
char buf[10];
|
||||
con->async_read_at_least(5,buf,5);
|
||||
BOOST_CHECK( con->ec == make_error_code(websocketpp::error::test) );
|
||||
@@ -324,7 +324,7 @@ BOOST_AUTO_TEST_CASE( eof_flag ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( fatal_error_flag ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
char buf[10];
|
||||
con->async_read_at_least(5,buf,5);
|
||||
BOOST_CHECK( con->ec == make_error_code(websocketpp::error::test) );
|
||||
@@ -333,7 +333,7 @@ BOOST_AUTO_TEST_CASE( fatal_error_flag ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( shared_pointer_memory_cleanup ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
stub_con::ptr con(new stub_con(true,a,e));
|
||||
|
||||
BOOST_CHECK_EQUAL(con.use_count(), 1);
|
||||
|
||||
|
||||
@@ -229,13 +229,6 @@ BOOST_AUTO_TEST_CASE( uri_valid_v6_mixed_case ) {
|
||||
BOOST_CHECK_EQUAL( uri.get_resource(), "/" );
|
||||
}
|
||||
|
||||
// Valid URI with a v6 address with mixed case
|
||||
BOOST_AUTO_TEST_CASE( uri_invalid_no_scheme ) {
|
||||
websocketpp::uri uri("myserver.com");
|
||||
|
||||
BOOST_CHECK( !uri.get_valid() );
|
||||
}
|
||||
|
||||
// Invalid IPv6 literal
|
||||
/*BOOST_AUTO_TEST_CASE( uri_invalid_v6_nonhex ) {
|
||||
websocketpp::uri uri("wss://[g::1]:9000/");
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
Broadcast Tutorial
|
||||
==================
|
||||
|
||||
This tutorial will dig into some more nitty gritty details on how to build high
|
||||
scalability, high performance websocket servers for broadcast like workflows.
|
||||
|
||||
Will go into features like:
|
||||
- minimizing work done in handlers
|
||||
- using asio thread pool mode
|
||||
- teaming multiple endpoints
|
||||
- setting accept queue depth
|
||||
- tuning compile time buffer sizes
|
||||
- prepared messages
|
||||
- flow control
|
||||
- basic operating system level tuning, particularly increasing file descriptor limits.
|
||||
- measuring performance with wsperf / autobahn
|
||||
- tuning permessage-deflate compression settings
|
||||
@@ -0,0 +1,45 @@
|
||||
WebSocket++ (0.3.0-alpha3)
|
||||
==========================
|
||||
|
||||
WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket
|
||||
Protocol. It allows integrating WebSocket client and server functionality into
|
||||
C++ programs. It uses interchangeable network transport modules including one
|
||||
based on C++ iostreams and one based on Boost Asio.
|
||||
|
||||
Major Features
|
||||
==============
|
||||
* Full support for RFC6455
|
||||
* Partial support for Hixie 76 / Hybi 00, 07-17 draft specs (server only)
|
||||
* Message/event based interface
|
||||
* Supports secure WebSockets (TLS), IPv6, and explicit proxies.
|
||||
* Flexible dependency management (C++11 Standard Library or Boost)
|
||||
* Interchangeable network transport modules (iostream and Boost Asio)
|
||||
* Portable/cross platform (Posix/Windows, 32/64bit, Intel/ARM/PPC)
|
||||
* Thread-safe
|
||||
|
||||
Get Involved
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/zaphoyd/websocketpp)
|
||||
|
||||
**Project Website**
|
||||
http://www.zaphoyd.com/websocketpp/
|
||||
|
||||
**User Manual**
|
||||
http://www.zaphoyd.com/websocketpp/manual/
|
||||
|
||||
**GitHub Repository**
|
||||
https://github.com/zaphoyd/websocketpp/
|
||||
|
||||
**Announcements Mailing List**
|
||||
http://groups.google.com/group/websocketpp-announcements/
|
||||
|
||||
**IRC Channel**
|
||||
#websocketpp (freenode)
|
||||
|
||||
**Discussion / Development / Support Mailing List / Forum**
|
||||
http://groups.google.com/group/websocketpp/
|
||||
|
||||
Author
|
||||
======
|
||||
Peter Thorson - websocketpp@zaphoyd.com
|
||||
@@ -11,3 +11,65 @@ Server
|
||||
- Subprotocol
|
||||
- Origin restrictions
|
||||
- HTTP statistics page
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Hi all,
|
||||
|
||||
I am working on some tutorials for WebSocket++ and was wondering if anyone here has specific features or concepts that they feel would be particularly useful to have covered? Anything that you wish there was a tutorial for now or wished there had been when you were first setting up your WebSocket++ application?
|
||||
|
||||
Best,
|
||||
|
||||
Peter
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket
|
||||
Protocol. It allows integrating WebSocket client and server functionality into
|
||||
C++ programs. It uses interchangeable network transport modules including one
|
||||
based on C++ iostreams and one based on Boost Asio.
|
||||
|
||||
Major Features
|
||||
==============
|
||||
* Full support for RFC6455
|
||||
* Partial support for Hixie 76 / Hybi 00, 07-17 draft specs (server only)
|
||||
* Message/event based interface
|
||||
* Supports secure WebSockets (TLS), IPv6, and explicit proxies.
|
||||
* Flexible dependency management (C++11 Standard Library or Boost)
|
||||
* Interchangeable network transport modules (iostream and Boost Asio)
|
||||
* Portable/cross platform (Posix/Windows, 32/64bit, Intel/ARM/PPC)
|
||||
* Thread-safe
|
||||
|
||||
Get Involved
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/zaphoyd/websocketpp)
|
||||
|
||||
**Project Website**
|
||||
http://www.zaphoyd.com/websocketpp/
|
||||
|
||||
**User Manual**
|
||||
http://www.zaphoyd.com/websocketpp/manual/
|
||||
|
||||
**GitHub Repository**
|
||||
https://github.com/zaphoyd/websocketpp/
|
||||
|
||||
**Announcements Mailing List**
|
||||
http://groups.google.com/group/websocketpp-announcements/
|
||||
|
||||
**IRC Channel**
|
||||
#websocketpp (freenode)
|
||||
|
||||
**Discussion / Development / Support Mailing List / Forum**
|
||||
http://groups.google.com/group/websocketpp/
|
||||
|
||||
Author
|
||||
======
|
||||
Peter Thorson - websocketpp@zaphoyd.com
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility client tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_client
|
||||
// directory of the WebSocket++ repository.
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility client tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_client
|
||||
// directory of the WebSocket++ repository.
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility client tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_client
|
||||
// directory of the WebSocket++ repository.
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
class websocket_endpoint {
|
||||
public:
|
||||
websocket_endpoint () {
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
}
|
||||
private:
|
||||
client m_endpoint;
|
||||
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
|
||||
};
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
websocket_endpoint endpoint;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility client tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_client
|
||||
// directory of the WebSocket++ repository.
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
class connection_metadata {
|
||||
public:
|
||||
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
|
||||
|
||||
connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
|
||||
: m_id(id)
|
||||
, m_hdl(hdl)
|
||||
, m_status("Connecting")
|
||||
, m_uri(uri)
|
||||
, m_server("N/A")
|
||||
{}
|
||||
|
||||
void on_open(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Open";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
}
|
||||
|
||||
void on_fail(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Failed";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
m_error_reason = con->get_ec().message();
|
||||
}
|
||||
|
||||
friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
|
||||
private:
|
||||
int m_id;
|
||||
websocketpp::connection_hdl m_hdl;
|
||||
std::string m_status;
|
||||
std::string m_uri;
|
||||
std::string m_server;
|
||||
std::string m_error_reason;
|
||||
};
|
||||
|
||||
std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
|
||||
out << "> URI: " << data.m_uri << "\n"
|
||||
<< "> Status: " << data.m_status << "\n"
|
||||
<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
|
||||
<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
class websocket_endpoint {
|
||||
public:
|
||||
websocket_endpoint () : m_next_id(0) {
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
}
|
||||
|
||||
int connect(std::string const & uri) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
|
||||
|
||||
if (ec) {
|
||||
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int new_id = m_next_id++;
|
||||
connection_metadata::ptr metadata_ptr(new connection_metadata(new_id, con->get_handle(), uri));
|
||||
m_connection_list[new_id] = metadata_ptr;
|
||||
|
||||
con->set_open_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_open,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
con->set_fail_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_fail,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
|
||||
m_endpoint.connect(con);
|
||||
|
||||
return new_id;
|
||||
}
|
||||
|
||||
connection_metadata::ptr get_metadata(int id) const {
|
||||
con_list::const_iterator metadata_it = m_connection_list.find(id);
|
||||
if (metadata_it == m_connection_list.end()) {
|
||||
return connection_metadata::ptr();
|
||||
} else {
|
||||
return metadata_it->second;
|
||||
}
|
||||
}
|
||||
private:
|
||||
typedef std::map<int,connection_metadata::ptr> con_list;
|
||||
|
||||
client m_endpoint;
|
||||
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
|
||||
|
||||
con_list m_connection_list;
|
||||
int m_next_id;
|
||||
};
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
websocket_endpoint endpoint;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "connect <ws uri>\n"
|
||||
<< "show <connection id>\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else if (input.substr(0,7) == "connect") {
|
||||
int id = endpoint.connect(input.substr(8));
|
||||
if (id != -1) {
|
||||
std::cout << "> Created connection with id " << id << std::endl;
|
||||
}
|
||||
} else if (input.substr(0,4) == "show") {
|
||||
int id = atoi(input.substr(5).c_str());
|
||||
|
||||
connection_metadata::ptr metadata = endpoint.get_metadata(id);
|
||||
if (metadata) {
|
||||
std::cout << *metadata << std::endl;
|
||||
} else {
|
||||
std::cout << "> Unknown connection id " << id << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "> Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
clang++ -std=c++11 -stdlib=libc++ -I/Users/zaphoyd/software/websocketpp/ -I/Users/zaphoyd/software/boost_1_55_0/ -D_WEBSOCKETPP_CPP11_STL_ step4.cpp /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_system.a
|
||||
|
||||
clang++ -I/Users/zaphoyd/software/websocketpp/ -I/Users/zaphoyd/software/boost_1_55_0/ step4.cpp /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_system.a /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_thread.a /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_random.a
|
||||
|
||||
clang++ -std=c++11 -stdlib=libc++ -I/Users/zaphoyd/Documents/websocketpp/ -I/Users/zaphoyd/Documents/boost_1_53_0_libcpp/ -D_WEBSOCKETPP_CPP11_STL_ step4.cpp /Users/zaphoyd/Documents/boost_1_53_0_libcpp/stage/lib/libboost_system.a
|
||||
|
||||
*/
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility client tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_client
|
||||
// directory of the WebSocket++ repository.
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
class connection_metadata {
|
||||
public:
|
||||
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
|
||||
|
||||
connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
|
||||
: m_id(id)
|
||||
, m_hdl(hdl)
|
||||
, m_status("Connecting")
|
||||
, m_uri(uri)
|
||||
, m_server("N/A")
|
||||
{}
|
||||
|
||||
void on_open(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Open";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
}
|
||||
|
||||
void on_fail(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Failed";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
m_error_reason = con->get_ec().message();
|
||||
}
|
||||
|
||||
void on_close(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Closed";
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
std::stringstream s;
|
||||
s << "close code: " << con->get_remote_close_code() << " ("
|
||||
<< websocketpp::close::status::get_string(con->get_remote_close_code())
|
||||
<< "), close reason: " << con->get_remote_close_reason();
|
||||
m_error_reason = s.str();
|
||||
}
|
||||
|
||||
websocketpp::connection_hdl get_hdl() const {
|
||||
return m_hdl;
|
||||
}
|
||||
|
||||
int get_id() const {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
std::string get_status() const {
|
||||
return m_status;
|
||||
}
|
||||
|
||||
friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
|
||||
private:
|
||||
int m_id;
|
||||
websocketpp::connection_hdl m_hdl;
|
||||
std::string m_status;
|
||||
std::string m_uri;
|
||||
std::string m_server;
|
||||
std::string m_error_reason;
|
||||
};
|
||||
|
||||
std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
|
||||
out << "> URI: " << data.m_uri << "\n"
|
||||
<< "> Status: " << data.m_status << "\n"
|
||||
<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
|
||||
<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
class websocket_endpoint {
|
||||
public:
|
||||
websocket_endpoint () : m_next_id(0) {
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
}
|
||||
|
||||
~websocket_endpoint() {
|
||||
m_endpoint.stop_perpetual();
|
||||
|
||||
for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
|
||||
if (it->second->get_status() != "Open") {
|
||||
// Only close open connections
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "> Closing connection " << it->second->get_id() << std::endl;
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
|
||||
if (ec) {
|
||||
std::cout << "> Error closing connection " << it->second->get_id() << ": "
|
||||
<< ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
int connect(std::string const & uri) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
|
||||
|
||||
if (ec) {
|
||||
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int new_id = m_next_id++;
|
||||
connection_metadata::ptr metadata_ptr(new connection_metadata(new_id, con->get_handle(), uri));
|
||||
m_connection_list[new_id] = metadata_ptr;
|
||||
|
||||
con->set_open_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_open,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
con->set_fail_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_fail,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
con->set_close_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_close,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
|
||||
m_endpoint.connect(con);
|
||||
|
||||
return new_id;
|
||||
}
|
||||
|
||||
void close(int id, websocketpp::close::status::value code, std::string reason) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
con_list::iterator metadata_it = m_connection_list.find(id);
|
||||
if (metadata_it == m_connection_list.end()) {
|
||||
std::cout << "> No connection found with id " << id << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_endpoint.close(metadata_it->second->get_hdl(), code, reason, ec);
|
||||
if (ec) {
|
||||
std::cout << "> Error initiating close: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
connection_metadata::ptr get_metadata(int id) const {
|
||||
con_list::const_iterator metadata_it = m_connection_list.find(id);
|
||||
if (metadata_it == m_connection_list.end()) {
|
||||
return connection_metadata::ptr();
|
||||
} else {
|
||||
return metadata_it->second;
|
||||
}
|
||||
}
|
||||
private:
|
||||
typedef std::map<int,connection_metadata::ptr> con_list;
|
||||
|
||||
client m_endpoint;
|
||||
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
|
||||
|
||||
con_list m_connection_list;
|
||||
int m_next_id;
|
||||
};
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
websocket_endpoint endpoint;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "connect <ws uri>\n"
|
||||
<< "close <connection id> [<close code:default=1000>] [<close reason>]\n"
|
||||
<< "show <connection id>\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else if (input.substr(0,7) == "connect") {
|
||||
int id = endpoint.connect(input.substr(8));
|
||||
if (id != -1) {
|
||||
std::cout << "> Created connection with id " << id << std::endl;
|
||||
}
|
||||
} else if (input.substr(0,5) == "close") {
|
||||
std::stringstream ss(input);
|
||||
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
endpoint.close(id, close_code, reason);
|
||||
} else if (input.substr(0,4) == "show") {
|
||||
int id = atoi(input.substr(5).c_str());
|
||||
|
||||
connection_metadata::ptr metadata = endpoint.get_metadata(id);
|
||||
if (metadata) {
|
||||
std::cout << *metadata << std::endl;
|
||||
} else {
|
||||
std::cout << "> Unknown connection id " << id << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "> Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
clang++ -std=c++11 -stdlib=libc++ -I/Users/zaphoyd/software/websocketpp/ -I/Users/zaphoyd/software/boost_1_55_0/ -D_WEBSOCKETPP_CPP11_STL_ step4.cpp /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_system.a
|
||||
|
||||
clang++ -I/Users/zaphoyd/software/websocketpp/ -I/Users/zaphoyd/software/boost_1_55_0/ step4.cpp /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_system.a /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_thread.a /Users/zaphoyd/software/boost_1_55_0/stage/lib/libboost_random.a
|
||||
|
||||
clang++ -std=c++11 -stdlib=libc++ -I/Users/zaphoyd/Documents/websocketpp/ -I/Users/zaphoyd/Documents/boost_1_53_0_libcpp/ -D_WEBSOCKETPP_CPP11_STL_ step4.cpp /Users/zaphoyd/Documents/boost_1_53_0_libcpp/stage/lib/libboost_system.a
|
||||
|
||||
*/
|
||||
@@ -1,719 +0,0 @@
|
||||
Utility Client Example Application
|
||||
==================================
|
||||
|
||||
Chapter 1: Initial Setup & Basics
|
||||
---------------------------------
|
||||
|
||||
Setting up the basic types, opening and closing connections, sending and receiving messages.
|
||||
|
||||
### Step 1
|
||||
|
||||
A basic program loop that prompts the user for a command and then processes it. In this tutorial we will modify this program to perform tasks and retrieve data from a remote server over a WebSocket connection.
|
||||
|
||||
#### Build
|
||||
`clang++ step1.cpp`
|
||||
|
||||
#### Code so far
|
||||
|
||||
*note* A code snapshot for each step is present next to this tutorial file in the git repository.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2
|
||||
|
||||
_Add WebSocket++ includes and set up an endpoint type._
|
||||
|
||||
WebSocket++ includes two major object types. The endpoint and the connection. The
|
||||
endpoint creates and launches new connections and maintains default settings for
|
||||
those connections. Endpoints also manage any shared network resources.
|
||||
|
||||
The connection stores information specific to each WebSocket session.
|
||||
|
||||
> **Note:** Once a connection is launched, there is no link between the endpoint and the connection. All default settings are copied into the new connection by the endpoint. Changing default settings on an endpoint will only affect future connections.
|
||||
Connections do not maintain a link back to their associated endpoint. Endpoints do not maintain a list of outstanding connections. If your application needs to iterate over all connections it will need to maintain a list of them itself.
|
||||
|
||||
WebSocket++ endpoints are built by combining an endpoint role with an endpoint config. There are two different types of endpoint roles, one each for the client and server roles in a WebSocket session. This is a client tutorial so we will use the client role `websocketpp::client` which is provided by the `<websocketpp/client.hpp>` header.
|
||||
|
||||
> ###### Terminology: Endpoint Config
|
||||
> WebSocket++ endpoints have a group of settings that may be configured at compile time via the `config` template parameter. A config is a struct that contains types and static constants that are used to produce an endpoint with specific properties. Depending on which config is being used the endpoint will have different methods available and may have additional third party dependencies.
|
||||
|
||||
The endpoint role takes a template parameter called `config` that is used to configure the behavior of endpoint at compile time. For this example we are going to use a default config provided by the library called `asio_client`, provided by `<websocketpp/config/asio_no_tls_client.hpp>`. This is a client config that uses boost::asio to provide network transport and does not support TLS based security. Later on we will discuss how to introduce TLS based security into a WebSocket++ application, more about the other stock configs, and how to build your own custom configs.
|
||||
|
||||
Combine a config with an endpoint role to produce a fully configured endpoint. This type will be used frequently so I would recommend a typedef here.
|
||||
|
||||
`typedef websocketpp::client<websocketpp::config::asio_client> client`
|
||||
|
||||
#### Build
|
||||
Adding WebSocket++ has added a few dependencies to our program that must be addressed in the build system. Firstly, the WebSocket++ and Boost library headers must be in the include search path of your build system. How exactly this is done depends on where you have the WebSocket++ headers installed and what build system you are using.
|
||||
|
||||
In addition to the new headers, boost::asio depends on the `boost_system` shared library. This will need to be added (either as a static or dynamic) to the linker. Refer to your build environment documentation for instructions on linking to shared libraries.
|
||||
|
||||
`clang++ step2.cpp -lboost_system`
|
||||
|
||||
#### Code so far
|
||||
```cpp
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Step 3
|
||||
|
||||
_Create endpoint wrapper object that handles initialization and setting up the background thread._
|
||||
|
||||
In order to process user input while network processing occurs in the background we are going to use a separate thread for the WebSocket++ processing loop. This leaves the main thread free to process foreground user input. In order to enable simple RAII style resource management for our thread and endpoint we will use a wrapper object that configures them both in its constructor.
|
||||
|
||||
> ###### Terminology: websocketpp::lib namespace
|
||||
> WebSocket++ is designed to be used with a C++11 standard library. As this is not universally available in popular build systems the Boost libraries may be used as polyfills for the C++11 standard library in C++98 build environments. The `websocketpp::lib` namespace is used by the library and its associated examples to abstract away the distinctions between the two. `websocketpp::lib::shared_ptr` will evaluate to `std::shared_ptr` in a C++11 environment and `boost::shared_ptr` otherwise.
|
||||
>
|
||||
> This tutorial uses the `websocketpp::lib` wrappers because it doesn't know what the build environment of the reader is. For your applications, unless you are interested in similar portability, are free to use the boost or std versions of these types directly.
|
||||
>
|
||||
>[TODO: link to more information about websocketpp::lib namespace and C++11 setup]
|
||||
|
||||
Within the `websocket_endpoint` constructor several things happen:
|
||||
|
||||
First, we set the endpoint logging behavior to silent by clearing all of the access and error logging channels. [TODO: link to more information about logging]
|
||||
```cpp
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
```
|
||||
|
||||
Next, we initialize the transport system underlying the endpoint and set it to perpetual mode. In perpetual mode the endpoint's processing loop will not exit automatically when it has no connections. This is important because we want this endpoint to remain active while our application is running and process requests for new WebSocket connections on demand as we need them. Both of these methods are specific to the asio transport. They will not be necessary or present in endpoints that use a non-asio config.
|
||||
```cpp
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
```
|
||||
|
||||
Finally, we launch a thread to run the `run` method of our client endpoint. While the endpoint is running it will process connection tasks (read and deliver incoming messages, frame and send outgoing messages, etc). Because it is running in perpetual mode, when there are no connections active it will wait for a new connection.
|
||||
```cpp
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
```
|
||||
|
||||
#### Build
|
||||
|
||||
Now that our client endpoint template is actually instantiated a few more linker dependencies will show up. In particular, WebSocket clients require a cryptographically secure random number generator. WebSocket++ is able to use either `boost_random` or the C++11 standard library <random> for this purpose. Because this example also uses threads, if we do not have C++11 std::thread available we will need to include `boost_thread`.
|
||||
|
||||
##### Clang (C++98 & boost)
|
||||
`clang++ step3.cpp -lboost_system -lboost_random -lboost_thread`
|
||||
|
||||
##### Clang (C++11)
|
||||
`clang++ -std=c++0x -stdlib=libc++ step3.cpp -lboost_system -D_WEBSOCKETPP_CPP11_STL_`
|
||||
|
||||
##### G++ (C++98 & Boost)
|
||||
`g++ step3.cpp -lboost_system -lboost_random -lboost_thread`
|
||||
|
||||
##### G++ v4.6+ (C++11)
|
||||
`g++ -std=c++0x step3.cpp -lboost_system -D_WEBSOCKETPP_CPP11_STL_`
|
||||
|
||||
#### Code so far
|
||||
|
||||
```cpp
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
class websocket_endpoint {
|
||||
public:
|
||||
websocket_endpoint () {
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
}
|
||||
private:
|
||||
client m_endpoint;
|
||||
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
|
||||
};
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
websocket_endpoint endpoint;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4
|
||||
|
||||
_Opening WebSocket connections_
|
||||
|
||||
This step adds two new commands to app_client. The ability to open a new connection and the ability to view information about a previously opened connection. Every connection that gets opened will be assigned an integer connection id that the user of the program can use to interact with that connection.
|
||||
|
||||
#### New Connection Metadata Object
|
||||
|
||||
In order to track information about each connection a `connection_metadata` object is defined. This object stores the numeric connection id and a number of fields that will be filled in as the connection is processed. Initially this includes the state of the connection (opening, open, failed, closed, etc), the original URI connected to, an identifying value from the server, and a description of the reason for connection failure/closure. Future steps will add more information to this metadata object.
|
||||
|
||||
#### Update `websocket_endpoint`
|
||||
|
||||
The `websocket_endpoint` object has gained some new data members and methods. It now tracks a mapping between connection IDs and their associated metadata as well as the next sequential ID number to hand out. The `connect()` method initiates a new connection. The `get_metadata` method retrieves metadata given an ID.
|
||||
|
||||
#### The connect method
|
||||
A new WebSocket connection is initiated via a three step process. First, a connection request is created by `endpoint::get_connection(uri)`. Next, the connection request is configured. Lastly, the connection request is submitted back to the endpoint via `endpoint::connect()` which adds it to the queue of new connections to make.
|
||||
|
||||
> ###### Terminology `connection_ptr`
|
||||
> WebSocket++ keeps track of connection related resources using a reference counted shared pointer. The type of this pointer is `endpoint::connection_ptr`. A `connection_ptr` allows direct access to information about the connection and allows changing connection settings. Because of this direct access and their internal resource management role within the library it is not safe to for end applications to use `connection_ptr` except in the specific circumstances detailed below.
|
||||
>
|
||||
> **When is it safe to use `connection_ptr`?**
|
||||
> - After `endpoint::get_connection(...)` and before `endpoint::connect()`: `get_connection` returns a `connection_ptr`. It is safe to use this pointer to configure your new connection. Once you submit the connection to `connect` you may no longer use the `connection_ptr` and should discard it immediately for optimal memory management.
|
||||
> - During a handler: WebSocket++ allows you to register hooks / callbacks / event handlers for specific events that happen during a connection's lifetime. During the invocation of one of these handlers the library guarantees that it is safe to use a `connection_ptr` for the connection associated with the currently running handler.
|
||||
|
||||
> ###### Terminology `connection_hdl`
|
||||
> Because of the limited thread safety of the `connection_ptr` the library also provides a more flexible connection identifier, the `connection_hdl`. The `connection_hdl` has type `websocketpp::connection_hdl` and it is defined in `<websocketpp/common/connection_hdl.hpp>`. Note that unlike `connection_ptr` this is not dependent on the type or config of the endpoint. Code that simply stores or transmits `connection_hdl` but does not use them can include only the header above and can treat its hdls like values.
|
||||
>
|
||||
> Connection handles are not used directly. They are used by endpoint methods to identify the target of the desired action. For example, the endpoint method that sends a new message will take as a parameter the hdl of the connection to send the message to.
|
||||
>
|
||||
> **When is it safe to use `connection_hdl`?**
|
||||
> `connection_hdl`s may be used at any time from any thread. They may be copied and stored in containers. Deleting a hdl will not affect the connection in any way. Handles may be upgraded to a `connection_ptr` during a handler call by using `endpoint::get_con_from_hdl()`. The resulting `connection_ptr` is safe to use for the duration of that handler invocation.
|
||||
>
|
||||
> **`connection_hdl` FAQs**
|
||||
> - `connection_hdl`s are guaranteed to be unique within a program. Multiple endpoints in a single program will always create connections with unique handles.
|
||||
> - Using a `connection_hdl` with a different endpoint than the one that created its associated connection will result in undefined behavior.
|
||||
> - Using a `connection_hdl` whose associated connection has been closed or deleted is safe. The endpoint will return a specific error saying the operation couldn't be completed because the associated connection doesn't exist.
|
||||
> [TODO: more here? link to a connection_hdl FAQ elsewhere?]
|
||||
|
||||
`websocket_endpoint::connect()` begins by calling `endpoint::get_connection()` using a uri passed as a parameter. Additionally, an error output value is passed to capture any errors that might occur during. If an error does occur an error notice is printed along with a descriptive message and the -1 / 'invalid' value is returned as the new ID.
|
||||
|
||||
> ###### Terminology: `error handling: exceptions vs error_code`
|
||||
> WebSocket++ uses the error code system defined by the C++11 `<system_error>` library. It can optionally fall back to a similar system provided by the Boost libraries. All user facing endpoint methods that can fail take an `error_code` in an output parameter and store the error that occured there before returning. An empty/default constructed value is returned in the case of success.
|
||||
>
|
||||
> **Exception throwing varients**
|
||||
> All user facing endpoint methods that take and use an `error_code` parameter have a version that throws an exception instead. These methods are identical in function and signature except for the lack of the final ec parameter. The type of the exception thrown is `websocketpp::exception`. This type derives from `std::exception` so it can be caught by catch blocks grabbing generic `std::exception`s. The `websocketpp::exception::code()` method may be used to extract the machine readable `error_code` value from an exception.
|
||||
>
|
||||
> For clarity about error handling the app_client example uses exclusively the exception free varients of these methods. Your application may choose to use either.
|
||||
|
||||
If connection creation succeeds, the next sequential connection ID is generated and a `connection_metadata` object is inserted into the connection list under that ID. Initially the metadata object stores the connection ID, the `connection_hdl`, and the URI the connection was opened to.
|
||||
|
||||
```cpp
|
||||
int new_id = m_next_id++;
|
||||
metadata_ptr metadata(new connection_metadata(new_id, con->get_handle(), uri));
|
||||
m_connection_list[new_id] = metadata;
|
||||
```
|
||||
|
||||
Next, the connection request is configured. For this step the only configuration we will do is setting up a few default handlers. Later on we will return and demonstrate some more detailed configuration that can happen here (setting user agents, origin, proxies, custom headers, subprotocols, etc).
|
||||
|
||||
> ###### Terminology: Registering handlers
|
||||
> WebSocket++ provides a number of execution points where you can register to have a handler run. Which of these points are available to your endpoint will depend on its config. TLS handlers will not exist on non-TLS endpoints for example. A complete list of handlers can be found at http://www.zaphoyd.com/websocketpp/manual/reference/handler-list.
|
||||
>
|
||||
> Handlers can be registered at the endpoint level and at the connection level. Endpoint handlers are copied into new connections as they are created. Changing an endpoint handler will affect only future connections. Handlers registered at the connection level will be bound to that specific connection only.
|
||||
>
|
||||
> The signature of handler binding methods is the same for endpoints and connections. The format is: `set_*_handler(...)`. Where * is the name of the handler. For example, `set_open_handler(...)` will set the handler to be called when a new connection is open. `set_fail_handler(...)` will set the handler to be called when a connection fails to connect.
|
||||
>
|
||||
> All handlers take one argument, a callable type that can be converted to a `std::function` with the correct count and type of arguments. You can pass free functions, functors, and Lambdas with matching argument lists as handlers. In addition, you can use `std::bind` (or `boost::bind`) to register functions with non-matching argument lists. This is useful for passing additional parameters not present in the handler signature or member functions that need to carry a 'this' pointer.
|
||||
>
|
||||
> The function signature of each handler can be looked up in the list above in the manual. In general, all handlers include the `connection_hdl` identifying which connection this even is associated with as the first parameter. Some handlers (such as the message handler) include additional parameters. Most handlers have a void return value but some (`validate`, `ping`, `tls_init`) do not. The specific meanings of the return values are documented in the handler list linked above.
|
||||
|
||||
`app_client` registers an open and a fail handler. We will use these to track whether each connection was successfully opened or failed. If it successfully opens, we will gather some information from the opening handshake and store it with our connection metadata.
|
||||
|
||||
In this example we are going to set connection specific handlers that are bound directly to the metadata object associated with our connection. This allows us to avoid performing a lookup in each handler to find the metadata object we plan to update which is a bit more efficient.
|
||||
|
||||
Lets look at the parameters being sent to bind in detail:
|
||||
|
||||
```cpp
|
||||
con->set_open_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_open,
|
||||
metadata,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
```
|
||||
|
||||
`&connection_metadata::on_open` is the address of the `on_open` member function of the `connection_metadata` class. `metadata_ptr` is a pointer to the `connection_metadata` object associated with this class. It will be used as the object on which the `on_open` member function will be called. `&m_endpoint` is the address of the endpoint in use. This parameter will be passed as-is to the `on_open` method. Lastly, `websocketpp::lib::placeholders::_1` is a placeholder indicating that the bound function should take one additional argument to be filled in at a later time. WebSocket++ will fill in this placeholder with the `connection_hdl` when it invokes the handler.
|
||||
|
||||
Finally, we call `endpoint::connect()` on our configured connection request and return the new connection ID.
|
||||
|
||||
#### Handler Member Functions
|
||||
|
||||
The open handler we registered, `connection_metadata::on_open`, sets the status metadata field to "Open" and retrieves the value of the "Server" header from the remote endpoint's HTTP response and stores it in the metadata object. Servers often set an identifying string in this header.
|
||||
|
||||
The fail handler we registered, `connection_metadata::on_fail`, sets the status metadata field to "Failed", the server field similarly to `on_open`, and retrieves the error code describing why the connection failed. The human readable message associated with that error code is saved to the metadata object.
|
||||
|
||||
#### New Commands
|
||||
|
||||
Two new commands have been set up. "connect [uri]" will pass the URI to the `websocket_endpoint` connect method and report an error or the connection ID of the new connection. "show [connection id]" will retrieve and print out the metadata associated with that connection. The help text has been updated accordingly.
|
||||
|
||||
```cpp
|
||||
} else if (input.substr(0,7) == "connect") {
|
||||
int id = endpoint.connect(input.substr(8));
|
||||
if (id != -1) {
|
||||
std::cout << "> Created connection with id " << id << std::endl;
|
||||
}
|
||||
} else if (input.substr(0,4) == "show") {
|
||||
int id = atoi(input.substr(5).c_str());
|
||||
|
||||
connection_metadata::ptr metadata = endpoint.get_metadata(id);
|
||||
if (metadata) {
|
||||
std::cout << *metadata << std::endl;
|
||||
} else {
|
||||
std::cout << "> Unknown connection id " << id << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Build
|
||||
|
||||
There are no changes to the build instructions from step 3
|
||||
|
||||
#### Run
|
||||
|
||||
```
|
||||
Enter Command: connect not a websocket uri
|
||||
> Connect initialization error: invalid uri
|
||||
Enter Command: show 0
|
||||
> Unknown connection id 0
|
||||
Enter Command: connect ws://echo.websocket.org
|
||||
> Created connection with id 0
|
||||
Enter Command: show 0
|
||||
> URI: ws://echo.websocket.org
|
||||
> Status: Open
|
||||
> Remote Server: Kaazing Gateway
|
||||
> Error/close reason: N/A
|
||||
Enter Command: connect ws://wikipedia.org
|
||||
> Created connection with id 1
|
||||
Enter Command: show 1
|
||||
> URI: ws://wikipedia.org
|
||||
> Status: Failed
|
||||
> Remote Server: Apache
|
||||
> Error/close reason: Invalid HTTP status.
|
||||
```
|
||||
|
||||
#### Code so far
|
||||
|
||||
```cpp
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
class connection_metadata {
|
||||
public:
|
||||
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
|
||||
|
||||
connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
|
||||
: m_id(id)
|
||||
, m_hdl(hdl)
|
||||
, m_status("Connecting")
|
||||
, m_uri(uri)
|
||||
, m_server("N/A")
|
||||
{}
|
||||
|
||||
void on_open(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Open";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
}
|
||||
|
||||
void on_fail(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Failed";
|
||||
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
m_server = con->get_response_header("Server");
|
||||
m_error_reason = con->get_ec().message();
|
||||
}
|
||||
|
||||
friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
|
||||
private:
|
||||
int m_id;
|
||||
websocketpp::connection_hdl m_hdl;
|
||||
std::string m_status;
|
||||
std::string m_uri;
|
||||
std::string m_server;
|
||||
std::string m_error_reason;
|
||||
};
|
||||
|
||||
std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
|
||||
out << "> URI: " << data.m_uri << "\n"
|
||||
<< "> Status: " << data.m_status << "\n"
|
||||
<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
|
||||
<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
class websocket_endpoint {
|
||||
public:
|
||||
websocket_endpoint () : m_next_id(0) {
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
}
|
||||
|
||||
int connect(std::string const & uri) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
|
||||
|
||||
if (ec) {
|
||||
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int new_id = m_next_id++;
|
||||
connection_metadata::ptr metadata_ptr(new connection_metadata(new_id, con->get_handle(), uri));
|
||||
m_connection_list[new_id] = metadata_ptr;
|
||||
|
||||
con->set_open_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_open,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
con->set_fail_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_fail,
|
||||
metadata_ptr,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
|
||||
m_endpoint.connect(con);
|
||||
|
||||
return new_id;
|
||||
}
|
||||
|
||||
connection_metadata::ptr get_metadata(int id) const {
|
||||
con_list::const_iterator metadata_it = m_connection_list.find(id);
|
||||
if (metadata_it == m_connection_list.end()) {
|
||||
return connection_metadata::ptr();
|
||||
} else {
|
||||
return metadata_it->second;
|
||||
}
|
||||
}
|
||||
private:
|
||||
typedef std::map<int,connection_metadata::ptr> con_list;
|
||||
|
||||
client m_endpoint;
|
||||
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
|
||||
|
||||
con_list m_connection_list;
|
||||
int m_next_id;
|
||||
};
|
||||
|
||||
int main() {
|
||||
bool done = false;
|
||||
std::string input;
|
||||
websocket_endpoint endpoint;
|
||||
|
||||
while (!done) {
|
||||
std::cout << "Enter Command: ";
|
||||
std::getline(std::cin, input);
|
||||
|
||||
if (input == "quit") {
|
||||
done = true;
|
||||
} else if (input == "help") {
|
||||
std::cout
|
||||
<< "\nCommand List:\n"
|
||||
<< "connect <ws uri>\n"
|
||||
<< "show <connection id>\n"
|
||||
<< "help: Display this help text\n"
|
||||
<< "quit: Exit the program\n"
|
||||
<< std::endl;
|
||||
} else if (input.substr(0,7) == "connect") {
|
||||
int id = endpoint.connect(input.substr(8));
|
||||
if (id != -1) {
|
||||
std::cout << "> Created connection with id " << id << std::endl;
|
||||
}
|
||||
} else if (input.substr(0,4) == "show") {
|
||||
int id = atoi(input.substr(5).c_str());
|
||||
|
||||
connection_metadata::ptr metadata = endpoint.get_metadata(id);
|
||||
if (metadata) {
|
||||
std::cout << *metadata << std::endl;
|
||||
} else {
|
||||
std::cout << "> Unknown connection id " << id << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "> Unrecognized Command" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5
|
||||
|
||||
_Closing connections_
|
||||
|
||||
This step adds a command that allows you to close a WebSocket connection and adjusts the quit command so that it cleanly closes all outstanding connections before quitting.
|
||||
|
||||
#### Getting connection close information out of WebSocket++
|
||||
|
||||
> ###### Terminology: WebSocket close codes & reasons
|
||||
> The WebSocket close handshake involves an exchange of optional machine readable close codes and human readable reason strings. Each endpoint sends independent close details. The codes are short integers. The reasons are UTF8 text strings of at most 125 characters. More details about valid close code ranges and the meaning of each code can be found at https://tools.ietf.org/html/rfc6455#section-7.4
|
||||
|
||||
The `websocketpp::close::status` namespace contains named constants for all of the IANA defined close codes. It also includes free functions to determine whether a value is reserved or invalid and to convert a code to a human readable text representation.
|
||||
|
||||
During the close handler call WebSocket++ connections offer the following methods for accessing close handshake information:
|
||||
|
||||
- `connection::get_remote_close_code()`: Get the close code as reported by the remote endpoint
|
||||
- `connection::get_remote_close_reason()`: Get the close reason as reported by the remote endpoint
|
||||
- `connection::get_local_close_code()`: Get the close code that this endpoint sent.
|
||||
- `connection::get_local_close_reason()`: Get the close reason that this endpoint sent.
|
||||
- `connection::get_ec()`: Get a more detailed/specific WebSocket++ `error_code` indicating what library error (if any) ultimately resulted in the connection closure.
|
||||
|
||||
*Note:* there are some special close codes that will report a code that was not actually sent on the wire. For example 1005/"no close code" indicates that the endpoint omitted a close code entirely and 1006/"abnormal close" indicates that there was a problem that resulted in the connection closing without having performed a close handshake.
|
||||
|
||||
#### Add close handler
|
||||
|
||||
The `connection_metadata::on_close` method is added. This method retrieves the close code and reason from the closing handshake and stores it in the local error reason field.
|
||||
|
||||
```cpp
|
||||
void on_close(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Closed";
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
std::stringstream s;
|
||||
s << "close code: " << con->get_remote_close_code() << " ("
|
||||
<< websocketpp::close::status::get_string(con->get_remote_close_code())
|
||||
<< "), close reason: " << con->get_remote_close_reason();
|
||||
m_error_reason = s.str();
|
||||
}
|
||||
```
|
||||
|
||||
Similarly to `on_open` and `on_fail`, `websocket_endpoint::connect` registers this close handler when a new connection is made.
|
||||
|
||||
#### Add close method to `websocket_endpoint`
|
||||
|
||||
This method starts by looking up the given connection ID in the connection list. Next a close request is sent to the connection's handle with the specified WebSocket close code. This is done by calling `endpoint::close`. This is a thread safe method that is used to asynchronously dispatch a close signal to the connection with the given handle. When the operation is complete the connection's close handler will be triggered.
|
||||
|
||||
```cpp
|
||||
void close(int id, websocketpp::close::status::value code) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
con_list::iterator metadata_it = m_connection_list.find(id);
|
||||
if (metadata_it == m_connection_list.end()) {
|
||||
std::cout << "> No connection found with id " << id << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_endpoint.close(metadata_it->second->get_hdl(), code, "", ec);
|
||||
if (ec) {
|
||||
std::cout << "> Error initiating close: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Add close option to the command loop and help message
|
||||
|
||||
A close option is added to the command loop. It takes a connection ID and optionally a close code and a close reason. If no code is specified the default of 1000/Normal is used. If no reason is specified, none is sent. The `endpoint::send` method will do some error checking and abort the close request if you try and send an invalid code or a reason with invalid UTF8 formatting. Reason strings longer than 125 characters will be truncated.
|
||||
|
||||
An entry is also added to the help system to describe how the new command may be used.
|
||||
|
||||
```cpp
|
||||
else if (input.substr(0,5) == "close") {
|
||||
std::stringstream ss(input);
|
||||
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
endpoint.close(id, close_code, reason);
|
||||
}
|
||||
```
|
||||
|
||||
#### Close all outstanding connections in `websocket_endpoint` destructor
|
||||
|
||||
Until now quitting the program left outstanding connections and the WebSocket++ network thread in a lurch. Now that we have a method of closing connections we can clean this up properly.
|
||||
|
||||
The destructor for `websocket_endpoint` now stops perpetual mode (so the run thread exits after the last connection is closed) and iterates through the list of open connections and requests a clean close for each. Finally, the run thread is joined which causes the program to wait until those connection closes complete.
|
||||
|
||||
```cpp
|
||||
~websocket_endpoint() {
|
||||
m_endpoint.stop_perpetual();
|
||||
|
||||
for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
|
||||
if (it->second->get_status() != "Open") {
|
||||
// Only close open connections
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "> Closing connection " << it->second->get_id() << std::endl;
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
|
||||
if (ec) {
|
||||
std::cout << "> Error closing connection " << it->second->get_id() << ": "
|
||||
<< ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
m_thread->join();
|
||||
}
|
||||
```
|
||||
|
||||
#### Build
|
||||
|
||||
There are no changes to the build instructions from step 4
|
||||
|
||||
#### Run
|
||||
|
||||
```
|
||||
Enter Command: connect ws://localhost:9002
|
||||
> Created connection with id 0
|
||||
Enter Command: close 0 1001 example message
|
||||
Enter Command: show 0
|
||||
> URI: ws://localhost:9002
|
||||
> Status: Closed
|
||||
> Remote Server: WebSocket++/0.3.0-alpha4
|
||||
> Error/close reason: close code: 1001 (Going away), close reason: example message
|
||||
Enter Command: connect ws://localhost:9002
|
||||
> Created connection with id 1
|
||||
Enter Command: close 1 1006
|
||||
> Error initiating close: Invalid close code used
|
||||
Enter Command: quit
|
||||
> Closing connection 1
|
||||
```
|
||||
|
||||
### Step 6
|
||||
|
||||
_Sending and receiving messages_
|
||||
|
||||
- Sending a messages
|
||||
- terminology: WebSocket opcodes, text vs binary messages
|
||||
- Receiving a message
|
||||
|
||||
### Step 7
|
||||
|
||||
_Using TLS / Secure WebSockets_
|
||||
|
||||
Chapter 2: Intermediate Features
|
||||
--------------------------------
|
||||
|
||||
### Step 8
|
||||
|
||||
_Intermediate level features_
|
||||
|
||||
- Subprotocol negotiation
|
||||
- Setting and reading custom headers
|
||||
- Ping and Pong
|
||||
- Proxies?
|
||||
- Setting user agent
|
||||
- Setting Origin
|
||||
- Timers and security
|
||||
- Close behavior
|
||||
- Send one message to all connections
|
||||
|
||||
|
||||
### Misc stuff not sure if it should be included here or elsewhere?
|
||||
|
||||
core websocket++ control flow.
|
||||
A handshake, followed by a split into 2 independent control strands
|
||||
- Handshake
|
||||
-- use information specified before the call to endpoint::connect to construct a WebSocket handshake request.
|
||||
-- Pass the WebSocket handshake request to the transport policy. The transport policy determines how to get these bytes to the endpoint playing the server role. Depending on which transport policy your endpoint uses this method will be different.
|
||||
-- Receive a handshake response from the underlying transport. This is parsed and checked for conformance to RFC6455. If the validation fails, the fail handler is called. Otherwise the open handler is called.
|
||||
- At this point control splits into two separate strands. One that reads new bytes from the transport policy on the incoming channle, the other that accepts new messages from the local application for framing and writing to the outgoing transport channel.
|
||||
- Read strand
|
||||
-- Read and process new bytes from transport
|
||||
-- If the bytes contain at least one complete message dispatch each message by calling the appropriate handler. This is either the message handler for data messages, or ping/pong/close handlers for each respective control message. If no handler is registered for a particular message it is ignored.
|
||||
-- Ask the transport layer for more bytes
|
||||
- Write strand
|
||||
-- Wait for messages from the application
|
||||
-- Perform error checking on message input,
|
||||
-- Frame message per RFC6455
|
||||
-- Queue message for sending
|
||||
-- Pass all outstanding messages to the transport policy for output
|
||||
-- When there are no messages left to send, return to waiting
|
||||
|
||||
Important observations
|
||||
Handlers run in line with library processing which has several implications applications should be aware of:
|
||||
@@ -51,26 +51,6 @@ namespace status {
|
||||
/// A blank value for internal use.
|
||||
static value const blank = 0;
|
||||
|
||||
/// Close the connection without a WebSocket close handshake.
|
||||
/**
|
||||
* This special value requests that the WebSocket connection be closed
|
||||
* without performing the WebSocket closing handshake. This does not comply
|
||||
* with RFC6455, but should be safe to do if necessary. This could be useful
|
||||
* for clients that need to disconnect quickly and cannot afford the
|
||||
* complete handshake.
|
||||
*/
|
||||
static value const omit_handshake = 1;
|
||||
|
||||
/// Close the connection with a forced TCP drop.
|
||||
/**
|
||||
* This special value requests that the WebSocket connection be closed by
|
||||
* forcibly dropping the TCP connection. This will leave the other side of
|
||||
* the connection with a broken connection and some expensive timeouts. this
|
||||
* should not be done except in extreme cases or in cases of malicious
|
||||
* remote endpoints.
|
||||
*/
|
||||
static value const force_tcp_drop = 2;
|
||||
|
||||
/// Normal closure, meaning that the purpose for which the connection was
|
||||
/// established has been fulfilled.
|
||||
static value const normal = 1000;
|
||||
@@ -199,46 +179,6 @@ namespace status {
|
||||
code == policy_violation || code == message_too_big ||
|
||||
code == internal_endpoint_error);
|
||||
}
|
||||
|
||||
/// Return a human readable interpretation of a WebSocket close code
|
||||
/**
|
||||
* See https://tools.ietf.org/html/rfc6455#section-7.4 for more details.
|
||||
*
|
||||
* @since 0.4.0-beta1
|
||||
*
|
||||
* @param [in] code The code to look up.
|
||||
* @return A human readable interpretation of the code.
|
||||
*/
|
||||
inline std::string get_string(value code) {
|
||||
switch (code) {
|
||||
case normal:
|
||||
return "Normal close";
|
||||
case going_away:
|
||||
return "Going away";
|
||||
case protocol_error:
|
||||
return "Protocol error";
|
||||
case unsupported_data:
|
||||
return "Unsupported data";
|
||||
case no_status:
|
||||
return "No status set";
|
||||
case abnormal_close:
|
||||
return "Abnormal close";
|
||||
case invalid_payload:
|
||||
return "Invalid payload";
|
||||
case policy_violation:
|
||||
return "Policy violoation";
|
||||
case message_too_big:
|
||||
return "Message too big";
|
||||
case extension_required:
|
||||
return "Extension required";
|
||||
case internal_endpoint_error:
|
||||
return "Internal endpoint error";
|
||||
case tls_handshake:
|
||||
return "TLS handshake failure";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
} // namespace status
|
||||
|
||||
/// Type used to convert close statuses between integer and wire representations
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace websocketpp {
|
||||
|
||||
/// A handle to uniquely identify a connection.
|
||||
/**
|
||||
* This type uniquely identifies a connection. It is implemented as a weak
|
||||
* This type uniquely identifies a connection. It is implimented as a weak
|
||||
* pointer to the connection in question. This provides uniqueness across
|
||||
* multiple endpoints and ensures that IDs never conflict or run out.
|
||||
*
|
||||
|
||||
@@ -41,11 +41,9 @@
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WEBSOCKETPP_CPP11_STL_) || __cplusplus >= 201103L
|
||||
// _WEBSOCKETPP_CPP11_STL_ is a flag from the build system that forces
|
||||
// WebSocket++ into C++11 mode. __cplusplus is a define set by the compiler
|
||||
// if it has full support for C++11 language features. If either are set use
|
||||
// C++11 language features
|
||||
#ifdef _WEBSOCKETPP_CPP11_STL_
|
||||
// This flag indicates that all of the C++11 language features are available
|
||||
// to us.
|
||||
#ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_
|
||||
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
|
||||
#endif
|
||||
@@ -55,9 +53,6 @@
|
||||
#ifndef _WEBSOCKETPP_INITIALIZER_LISTS_
|
||||
#define _WEBSOCKETPP_INITIALIZER_LISTS_
|
||||
#endif
|
||||
#ifndef _WEBSOCKETPP_NULLPTR_TOKEN_
|
||||
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
|
||||
#endif
|
||||
#else
|
||||
// Test for noexcept
|
||||
#ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_
|
||||
@@ -95,25 +90,6 @@
|
||||
#if __has_feature(cxx_generalized_initializers) && !defined(_WEBSOCKETPP_INITIALIZER_LISTS_)
|
||||
#define _WEBSOCKETPP_INITIALIZER_LISTS_
|
||||
#endif
|
||||
|
||||
// Test for nullptr
|
||||
#ifndef _WEBSOCKETPP_NULLPTR_TOKEN_
|
||||
#ifdef _WEBSOCKETPP_NULLPTR_
|
||||
// build system says we have nullptr
|
||||
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
|
||||
#else
|
||||
#if __has_feature(cxx_nullptr)
|
||||
// clang feature detect says we have nullptr
|
||||
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
|
||||
#elif _MSC_VER >= 1600
|
||||
// Visual Studio version that has nullptr
|
||||
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
|
||||
#else
|
||||
// assume we don't have nullptr
|
||||
#define _WEBSOCKETPP_NULLPTR_TOKEN_ 0
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // WEBSOCKETPP_COMMON_CPP11_HPP
|
||||
+70
-70
@@ -34,7 +34,7 @@
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
@@ -45,12 +45,12 @@
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Removed support for non-ANSI compilers; removed
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
@@ -80,9 +80,9 @@ typedef unsigned int md5_word_t; /* 32-bit word */
|
||||
|
||||
/* Define the state of the MD5 Algorithm. */
|
||||
typedef struct md5_state_s {
|
||||
md5_word_t count[2]; /* message length in bits, lsw first */
|
||||
md5_word_t abcd[4]; /* digest buffer */
|
||||
md5_byte_t buf[64]; /* accumulate block */
|
||||
md5_word_t count[2]; /* message length in bits, lsw first */
|
||||
md5_word_t abcd[4]; /* digest buffer */
|
||||
md5_byte_t buf[64]; /* accumulate block */
|
||||
} md5_state_t;
|
||||
|
||||
/* Initialize the algorithm. */
|
||||
@@ -94,7 +94,7 @@ inline void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes)
|
||||
/* Finish the message and return the digest. */
|
||||
inline void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
||||
|
||||
#undef ZSW_MD5_BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#undef ZSW_MD5_BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#ifdef ARCH_IS_BIG_ENDIAN
|
||||
# define ZSW_MD5_BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
|
||||
#else
|
||||
@@ -169,8 +169,8 @@ inline void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
||||
|
||||
static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) {
|
||||
md5_word_t
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
md5_word_t t;
|
||||
#if ZSW_MD5_BYTE_ORDER > 0
|
||||
/* Define storage only for big-endian CPUs. */
|
||||
@@ -183,51 +183,51 @@ static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) {
|
||||
|
||||
{
|
||||
#if ZSW_MD5_BYTE_ORDER == 0
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static int const w = 1;
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static int const w = 1;
|
||||
|
||||
if (*((md5_byte_t const *)&w)) /* dynamic little-endian */
|
||||
if (*((md5_byte_t const *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if ZSW_MD5_BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (md5_byte_t const *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (md5_word_t const *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
std::memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#if ZSW_MD5_BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (md5_byte_t const *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (md5_word_t const *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
std::memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if ZSW_MD5_BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if ZSW_MD5_BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
#if ZSW_MD5_BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
|
||||
# if ZSW_MD5_BYTE_ORDER == 0
|
||||
X = xbuf; /* (dynamic only) */
|
||||
X = xbuf; /* (dynamic only) */
|
||||
# else
|
||||
# define xbuf X /* (static only) */
|
||||
# define xbuf X /* (static only) */
|
||||
# endif
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -361,71 +361,71 @@ void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes) {
|
||||
md5_word_t nbits = (md5_word_t)(nbytes << 3);
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
return;
|
||||
|
||||
/* Update the message length. */
|
||||
pms->count[1] += nbytes >> 29;
|
||||
pms->count[0] += nbits;
|
||||
if (pms->count[0] < nbits)
|
||||
pms->count[1]++;
|
||||
pms->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : static_cast<int>(nbytes));
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : static_cast<int>(nbytes));
|
||||
|
||||
std::memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
std::memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64)
|
||||
md5_process(pms, p);
|
||||
md5_process(pms, p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
std::memcpy(pms->buf, p, left);
|
||||
std::memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) {
|
||||
static md5_byte_t const pad[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
md5_byte_t data[8];
|
||||
int i;
|
||||
|
||||
/* Save the length before padding. */
|
||||
for (i = 0; i < 8; ++i)
|
||||
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
|
||||
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
|
||||
/* Pad to 56 bytes mod 64. */
|
||||
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
md5_append(pms, data, 8);
|
||||
for (i = 0; i < 16; ++i)
|
||||
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
||||
|
||||
// some convenience c++ functions
|
||||
inline std::string md5_hash_string(std::string const & s) {
|
||||
char digest[16];
|
||||
char digest[16];
|
||||
|
||||
md5_state_t state;
|
||||
md5_state_t state;
|
||||
|
||||
md5_init(&state);
|
||||
md5_append(&state, (md5_byte_t const *)s.c_str(), s.size());
|
||||
md5_finish(&state, (md5_byte_t *)digest);
|
||||
md5_init(&state);
|
||||
md5_append(&state, (md5_byte_t const *)s.c_str(), s.size());
|
||||
md5_finish(&state, (md5_byte_t *)digest);
|
||||
|
||||
std::string ret;
|
||||
ret.resize(16);
|
||||
std::copy(digest,digest+16,ret.begin());
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#define WEBSOCKETPP_COMMON_NETWORK_HPP
|
||||
|
||||
// For ntohs and htons
|
||||
#if defined(_WIN32)
|
||||
#if defined(WIN32)
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
//#include <arpa/inet.h>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* don't fit somewhere else better.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#if defined(WIN32) && !defined(NOMINMAX)
|
||||
// don't define min and max macros that conflict with std::min and std::max
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#if defined (_WIN32) && defined (_MSC_VER) && (_MSC_VER < 1600)
|
||||
#if WIN32 && (_MSC_VER < 1600)
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
using boost::int8_t;
|
||||
|
||||
@@ -215,18 +215,6 @@ struct core {
|
||||
*/
|
||||
static const bool silent_close = false;
|
||||
|
||||
/// Default maximum message size
|
||||
/**
|
||||
* Default value for the processor's maximum message size. Maximum message size
|
||||
* determines the point at which the library will fail a connection with the
|
||||
* message_too_big protocol error.
|
||||
*
|
||||
* The default is 32MB
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
static const size_t max_message_size = 32000000;
|
||||
|
||||
/// Global flag for enabling/disabling extensions
|
||||
static const bool enable_extensions = true;
|
||||
|
||||
|
||||
@@ -34,11 +34,7 @@
|
||||
#include <websocketpp/common/stdint.hpp>
|
||||
|
||||
// Concurrency
|
||||
#ifndef _WEBSOCKETPP_NO_THREADING_
|
||||
#include <websocketpp/concurrency/basic.hpp>
|
||||
#else
|
||||
#include <websocketpp/concurrency/none.hpp>
|
||||
#endif
|
||||
|
||||
// Transport
|
||||
#include <websocketpp/transport/iostream/endpoint.hpp>
|
||||
@@ -72,11 +68,7 @@ struct core_client {
|
||||
typedef core_client type;
|
||||
|
||||
// Concurrency policy
|
||||
#ifndef _WEBSOCKETPP_NO_THREADING_
|
||||
typedef websocketpp::concurrency::basic concurrency_type;
|
||||
#else
|
||||
typedef websocketpp::concurrency::none concurrency_type;
|
||||
#endif
|
||||
|
||||
// HTTP Parser Policies
|
||||
typedef http::parser::request request_type;
|
||||
@@ -224,18 +216,6 @@ struct core_client {
|
||||
*/
|
||||
static const bool silent_close = false;
|
||||
|
||||
/// Default maximum message size
|
||||
/**
|
||||
* Default value for the processor's maximum message size. Maximum message size
|
||||
* determines the point at which the library will fail a connection with the
|
||||
* message_too_big protocol error.
|
||||
*
|
||||
* The default is 32MB
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
static const size_t max_message_size = 32000000;
|
||||
|
||||
/// Global flag for enabling/disabling extensions
|
||||
static const bool enable_extensions = true;
|
||||
|
||||
|
||||
@@ -216,18 +216,6 @@ struct debug_core {
|
||||
*/
|
||||
static const bool silent_close = false;
|
||||
|
||||
/// Default maximum message size
|
||||
/**
|
||||
* Default value for the processor's maximum message size. Maximum message size
|
||||
* determines the point at which the library will fail a connection with the
|
||||
* message_too_big protocol error.
|
||||
*
|
||||
* The default is 32MB
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
static const size_t max_message_size = 32000000;
|
||||
|
||||
/// Global flag for enabling/disabling extensions
|
||||
static const bool enable_extensions = true;
|
||||
|
||||
|
||||
+11
-176
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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:
|
||||
@@ -42,7 +42,6 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -280,9 +279,9 @@ private:
|
||||
};
|
||||
public:
|
||||
|
||||
explicit connection(bool p_is_server, std::string const & ua, alog_type& alog,
|
||||
explicit connection(bool is_server, std::string const & ua, alog_type& alog,
|
||||
elog_type& elog, rng_type & rng)
|
||||
: transport_con_type(p_is_server, alog, elog)
|
||||
: transport_con_type(is_server,alog,elog)
|
||||
, m_handle_read_frame(lib::bind(
|
||||
&type::handle_read_frame,
|
||||
this,
|
||||
@@ -295,17 +294,12 @@ public:
|
||||
lib::placeholders::_1
|
||||
))
|
||||
, m_user_agent(ua)
|
||||
, m_open_handshake_timeout_dur(config::timeout_open_handshake)
|
||||
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
|
||||
, m_pong_timeout_dur(config::timeout_pong)
|
||||
, m_max_message_size(config::max_message_size)
|
||||
, 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)
|
||||
, m_write_flag(false)
|
||||
, m_read_flag(true)
|
||||
, m_is_server(p_is_server)
|
||||
, m_is_server(is_server)
|
||||
, m_alog(alog)
|
||||
, m_elog(elog)
|
||||
, m_rng(rng)
|
||||
@@ -458,111 +452,6 @@ public:
|
||||
m_message_handler = h;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Connection timeouts and other limits //
|
||||
//////////////////////////////////////////
|
||||
|
||||
/// Set open handshake timeout
|
||||
/**
|
||||
* Sets the length of time the library will wait after an opening handshake
|
||||
* has been initiated before cancelling it. This can be used to prevent
|
||||
* excessive wait times for outgoing clients or excessive resource usage
|
||||
* from broken clients or DoS attacks on servers.
|
||||
*
|
||||
* Connections that time out will have their fail handlers called with the
|
||||
* open_handshake_timeout error code.
|
||||
*
|
||||
* The default value is specified via the compile time config value
|
||||
* 'timeout_open_handshake'. The default value in the core config
|
||||
* is 5000ms. A value of 0 will disable the timer entirely.
|
||||
*
|
||||
* To be effective, the transport you are using must support timers. See
|
||||
* the documentation for your transport policy for details about its
|
||||
* timer support.
|
||||
*
|
||||
* @param dur The length of the open handshake timeout in ms
|
||||
*/
|
||||
void set_open_handshake_timeout(long dur) {
|
||||
m_open_handshake_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Set close handshake timeout
|
||||
/**
|
||||
* Sets the length of time the library will wait after a closing handshake
|
||||
* has been initiated before cancelling it. This can be used to prevent
|
||||
* excessive wait times for outgoing clients or excessive resource usage
|
||||
* from broken clients or DoS attacks on servers.
|
||||
*
|
||||
* Connections that time out will have their close handlers called with the
|
||||
* close_handshake_timeout error code.
|
||||
*
|
||||
* The default value is specified via the compile time config value
|
||||
* 'timeout_close_handshake'. The default value in the core config
|
||||
* is 5000ms. A value of 0 will disable the timer entirely.
|
||||
*
|
||||
* To be effective, the transport you are using must support timers. See
|
||||
* the documentation for your transport policy for details about its
|
||||
* timer support.
|
||||
*
|
||||
* @param dur The length of the close handshake timeout in ms
|
||||
*/
|
||||
void set_close_handshake_timeout(long dur) {
|
||||
m_close_handshake_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Set pong timeout
|
||||
/**
|
||||
* Sets the length of time the library will wait for a pong response to a
|
||||
* ping. This can be used as a keepalive or to detect broken connections.
|
||||
*
|
||||
* Pong responses that time out will have the pong timeout handler called.
|
||||
*
|
||||
* The default value is specified via the compile time config value
|
||||
* 'timeout_pong'. The default value in the core config
|
||||
* is 5000ms. A value of 0 will disable the timer entirely.
|
||||
*
|
||||
* To be effective, the transport you are using must support timers. See
|
||||
* the documentation for your transport policy for details about its
|
||||
* timer support.
|
||||
*
|
||||
* @param dur The length of the pong timeout in ms
|
||||
*/
|
||||
void set_pong_timeout(long dur) {
|
||||
m_pong_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Get maximum message size
|
||||
/**
|
||||
* Get maximum message size. Maximum message size determines the point at which the
|
||||
* connection will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is set by the endpoint that creates the connection.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
size_t get_max_message_size() const {
|
||||
return m_max_message_size;
|
||||
}
|
||||
|
||||
/// Set maximum message size
|
||||
/**
|
||||
* Set maximum message size. Maximum message size determines the point at which the
|
||||
* connection will fail a connection with the message_too_big protocol error. This
|
||||
* value may be changed during the connection.
|
||||
*
|
||||
* The default is set by the endpoint that creates the connection.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param new_value The value to set as the maximum message size.
|
||||
*/
|
||||
void set_max_message_size(size_t new_value) {
|
||||
m_max_message_size = new_value;
|
||||
if (m_processor) {
|
||||
m_processor->set_max_message_size(new_value);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// Uncategorized public methods //
|
||||
//////////////////////////////////
|
||||
@@ -650,46 +539,9 @@ public:
|
||||
* @return An error code
|
||||
*/
|
||||
lib::error_code interrupt();
|
||||
|
||||
|
||||
/// Transport inturrupt callback
|
||||
void handle_interrupt();
|
||||
|
||||
/// Pause reading of new data
|
||||
/**
|
||||
* Signals to the connection to halt reading of new data. While reading is paused,
|
||||
* the connection will stop reading from its associated socket. In turn this will
|
||||
* result in TCP based flow control kicking in and slowing data flow from the remote
|
||||
* endpoint.
|
||||
*
|
||||
* This is useful for applications that push new requests to a queue to be processed
|
||||
* by another thread and need a way to signal when their request queue is full without
|
||||
* blocking the network processing thread.
|
||||
*
|
||||
* Use `resume_reading()` to resume.
|
||||
*
|
||||
* If supported by the transport this is done asynchronously. As such reading may not
|
||||
* stop until the current read operation completes. Typically you can expect to
|
||||
* receive no more bytes after initiating a read pause than the size of the read
|
||||
* buffer.
|
||||
*
|
||||
* If reading is paused for this connection already nothing is changed.
|
||||
*/
|
||||
lib::error_code pause_reading();
|
||||
|
||||
/// Pause reading callback
|
||||
void handle_pause_reading();
|
||||
|
||||
/// Resume reading of new data
|
||||
/**
|
||||
* Signals to the connection to resume reading of new data after it was paused by
|
||||
* `pause_reading()`.
|
||||
*
|
||||
* If reading is not paused for this connection already nothing is changed.
|
||||
*/
|
||||
lib::error_code resume_reading();
|
||||
|
||||
/// Resume reading callback
|
||||
void handle_resume_reading();
|
||||
|
||||
/// Send a ping
|
||||
/**
|
||||
@@ -1146,7 +998,7 @@ public:
|
||||
message_ptr get_message(websocketpp::frame::opcode::value op, size_t size)
|
||||
const
|
||||
{
|
||||
return m_msg_manager->get_message(op, size);
|
||||
return m_msg_manager->get_outgoing_message(op, size);
|
||||
}
|
||||
|
||||
void start();
|
||||
@@ -1164,8 +1016,8 @@ public:
|
||||
void handle_open_handshake_timeout(lib::error_code const & ec);
|
||||
void handle_close_handshake_timeout(lib::error_code const & ec);
|
||||
|
||||
void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred);
|
||||
void read_frame();
|
||||
void handle_read_frame(lib::error_code const & ec,
|
||||
size_t bytes_transferred);
|
||||
|
||||
/// Get array of WebSocket protocol versions that this connection supports.
|
||||
const std::vector<int>& get_supported_versions() const;
|
||||
@@ -1353,20 +1205,12 @@ private:
|
||||
*/
|
||||
void log_fail_result();
|
||||
|
||||
/// Prints information about an arbitrary error code on the specified channel
|
||||
template <typename error_type>
|
||||
void log_err(log::level l, char const * msg, error_type const & ec) {
|
||||
std::stringstream s;
|
||||
s << msg << " error: " << ec << " (" << ec.message() << ")";
|
||||
m_elog.write(l, s.str());
|
||||
}
|
||||
|
||||
// internal handler functions
|
||||
read_handler m_handle_read_frame;
|
||||
write_frame_handler m_write_frame_handler;
|
||||
|
||||
// static settings
|
||||
std::string const m_user_agent;
|
||||
const std::string m_user_agent;
|
||||
|
||||
/// Pointer to the connection handle
|
||||
connection_hdl m_connection_hdl;
|
||||
@@ -1383,12 +1227,6 @@ private:
|
||||
validate_handler m_validate_handler;
|
||||
message_handler m_message_handler;
|
||||
|
||||
/// constant values
|
||||
long m_open_handshake_timeout_dur;
|
||||
long m_close_handshake_timeout_dur;
|
||||
long m_pong_timeout_dur;
|
||||
size_t m_max_message_size;
|
||||
|
||||
/// External connection state
|
||||
/**
|
||||
* Lock: m_connection_state_lock
|
||||
@@ -1451,9 +1289,9 @@ private:
|
||||
*/
|
||||
std::vector<transport::buffer> m_send_buffer;
|
||||
|
||||
/// a list of pointers to hold on to the messages being written to keep them
|
||||
/// a pointer to hold on to the current message being written to keep it
|
||||
/// from going out of scope before the write is complete.
|
||||
std::vector<message_ptr> m_current_msgs;
|
||||
message_ptr m_current_msg;
|
||||
|
||||
/// True if there is currently an outstanding transport write
|
||||
/**
|
||||
@@ -1461,9 +1299,6 @@ private:
|
||||
*/
|
||||
bool m_write_flag;
|
||||
|
||||
/// True if this connection is presently reading new data
|
||||
bool m_read_flag;
|
||||
|
||||
// connection data
|
||||
request_type m_request;
|
||||
response_type m_response;
|
||||
|
||||
+5
-157
@@ -87,22 +87,18 @@ public:
|
||||
|
||||
typedef lib::shared_ptr<connection_weak_ptr> hdl_type;
|
||||
|
||||
explicit endpoint(bool p_is_server)
|
||||
explicit endpoint(bool is_server)
|
||||
: m_alog(config::alog_level, &std::cout)
|
||||
, m_elog(config::elog_level, &std::cerr)
|
||||
, m_user_agent(::websocketpp::user_agent)
|
||||
, m_open_handshake_timeout_dur(config::timeout_open_handshake)
|
||||
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
|
||||
, m_pong_timeout_dur(config::timeout_pong)
|
||||
, m_max_message_size(config::max_message_size)
|
||||
, m_is_server(p_is_server)
|
||||
, m_is_server(is_server)
|
||||
{
|
||||
m_alog.set_channels(config::alog_level);
|
||||
m_elog.set_channels(config::elog_level);
|
||||
|
||||
m_alog.write(log::alevel::devel, "endpoint constructor");
|
||||
m_alog.write(log::alevel::devel,"endpoint constructor");
|
||||
|
||||
transport_type::init_logging(&m_alog, &m_elog);
|
||||
transport_type::init_logging(&m_alog,&m_elog);
|
||||
}
|
||||
|
||||
/// Returns the user agent string that this endpoint will use
|
||||
@@ -273,112 +269,6 @@ public:
|
||||
m_message_handler = h;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Connection timeouts and other limits //
|
||||
//////////////////////////////////////////
|
||||
|
||||
/// Set open handshake timeout
|
||||
/**
|
||||
* Sets the length of time the library will wait after an opening handshake
|
||||
* has been initiated before cancelling it. This can be used to prevent
|
||||
* excessive wait times for outgoing clients or excessive resource usage
|
||||
* from broken clients or DoS attacks on servers.
|
||||
*
|
||||
* Connections that time out will have their fail handlers called with the
|
||||
* open_handshake_timeout error code.
|
||||
*
|
||||
* The default value is specified via the compile time config value
|
||||
* 'timeout_open_handshake'. The default value in the core config
|
||||
* is 5000ms. A value of 0 will disable the timer entirely.
|
||||
*
|
||||
* To be effective, the transport you are using must support timers. See
|
||||
* the documentation for your transport policy for details about its
|
||||
* timer support.
|
||||
*
|
||||
* @param dur The length of the open handshake timeout in ms
|
||||
*/
|
||||
void set_open_handshake_timeout(long dur) {
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_open_handshake_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Set close handshake timeout
|
||||
/**
|
||||
* Sets the length of time the library will wait after a closing handshake
|
||||
* has been initiated before cancelling it. This can be used to prevent
|
||||
* excessive wait times for outgoing clients or excessive resource usage
|
||||
* from broken clients or DoS attacks on servers.
|
||||
*
|
||||
* Connections that time out will have their close handlers called with the
|
||||
* close_handshake_timeout error code.
|
||||
*
|
||||
* The default value is specified via the compile time config value
|
||||
* 'timeout_close_handshake'. The default value in the core config
|
||||
* is 5000ms. A value of 0 will disable the timer entirely.
|
||||
*
|
||||
* To be effective, the transport you are using must support timers. See
|
||||
* the documentation for your transport policy for details about its
|
||||
* timer support.
|
||||
*
|
||||
* @param dur The length of the close handshake timeout in ms
|
||||
*/
|
||||
void set_close_handshake_timeout(long dur) {
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_close_handshake_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Set pong timeout
|
||||
/**
|
||||
* Sets the length of time the library will wait for a pong response to a
|
||||
* ping. This can be used as a keepalive or to detect broken connections.
|
||||
*
|
||||
* Pong responses that time out will have the pong timeout handler called.
|
||||
*
|
||||
* The default value is specified via the compile time config value
|
||||
* 'timeout_pong'. The default value in the core config
|
||||
* is 5000ms. A value of 0 will disable the timer entirely.
|
||||
*
|
||||
* To be effective, the transport you are using must support timers. See
|
||||
* the documentation for your transport policy for details about its
|
||||
* timer support.
|
||||
*
|
||||
* @param dur The length of the pong timeout in ms
|
||||
*/
|
||||
void set_pong_timeout(long dur) {
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_pong_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Get default maximum message size
|
||||
/**
|
||||
* Get the default maximum message size that will be used for new connections created
|
||||
* by this endpoint. The maximum message size determines the point at which the
|
||||
* connection will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is set by the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
size_t get_max_message_size() const {
|
||||
return m_max_message_size;
|
||||
}
|
||||
|
||||
/// Set default maximum message size
|
||||
/**
|
||||
* Set the default maximum message size that will be used for new connections created
|
||||
* by this endpoint. Maximum message size determines the point at which the connection
|
||||
* will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is set by the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param new_value The value to set as the maximum message size.
|
||||
*/
|
||||
void set_max_message_size(size_t new_value) {
|
||||
m_max_message_size = new_value;
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/* Connection pass through functions */
|
||||
/*************************************/
|
||||
@@ -394,43 +284,6 @@ public:
|
||||
void interrupt(connection_hdl hdl, lib::error_code & ec);
|
||||
void interrupt(connection_hdl hdl);
|
||||
|
||||
/// Pause reading of new data (exception free)
|
||||
/**
|
||||
* Signals to the connection to halt reading of new data. While reading is paused,
|
||||
* the connection will stop reading from its associated socket. In turn this will
|
||||
* result in TCP based flow control kicking in and slowing data flow from the remote
|
||||
* endpoint.
|
||||
*
|
||||
* This is useful for applications that push new requests to a queue to be processed
|
||||
* by another thread and need a way to signal when their request queue is full without
|
||||
* blocking the network processing thread.
|
||||
*
|
||||
* Use `resume_reading()` to resume.
|
||||
*
|
||||
* If supported by the transport this is done asynchronously. As such reading may not
|
||||
* stop until the current read operation completes. Typically you can expect to
|
||||
* receive no more bytes after initiating a read pause than the size of the read
|
||||
* buffer.
|
||||
*
|
||||
* If reading is paused for this connection already nothing is changed.
|
||||
*/
|
||||
void pause_reading(connection_hdl hdl, lib::error_code & ec);
|
||||
|
||||
/// Pause reading of new data
|
||||
void pause_reading(connection_hdl hdl);
|
||||
|
||||
/// Resume reading of new data (exception free)
|
||||
/**
|
||||
* Signals to the connection to resume reading of new data after it was paused by
|
||||
* `pause_reading()`.
|
||||
*
|
||||
* If reading is not paused for this connection already nothing is changed.
|
||||
*/
|
||||
void resume_reading(connection_hdl hdl, lib::error_code & ec);
|
||||
|
||||
/// Resume reading of new data
|
||||
void resume_reading(connection_hdl hdl);
|
||||
|
||||
/// Create a message and add it to the outgoing send queue (exception free)
|
||||
/**
|
||||
* Convenience method to send a message given a payload string and an opcode
|
||||
@@ -562,18 +415,13 @@ private:
|
||||
validate_handler m_validate_handler;
|
||||
message_handler m_message_handler;
|
||||
|
||||
long m_open_handshake_timeout_dur;
|
||||
long m_close_handshake_timeout_dur;
|
||||
long m_pong_timeout_dur;
|
||||
size_t m_max_message_size;
|
||||
|
||||
rng_type m_rng;
|
||||
|
||||
// static settings
|
||||
bool const m_is_server;
|
||||
|
||||
// endpoint state
|
||||
mutable mutex_type m_mutex;
|
||||
mutex_type m_mutex;
|
||||
};
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
+4
-15
@@ -114,14 +114,7 @@ enum value {
|
||||
close_handshake_timeout,
|
||||
|
||||
/// Invalid port in URI
|
||||
invalid_port,
|
||||
|
||||
/// An async accept operation failed because the underlying transport has been
|
||||
/// requested to not listen for new connections anymore.
|
||||
async_accept_not_listening,
|
||||
|
||||
/// The requested operation was canceled
|
||||
operation_canceled
|
||||
invalid_port
|
||||
}; // enum value
|
||||
|
||||
|
||||
@@ -183,10 +176,6 @@ public:
|
||||
return "The closing handshake timed out";
|
||||
case error::invalid_port:
|
||||
return "Invalid URI port";
|
||||
case error::async_accept_not_listening:
|
||||
return "Async Accept not listening";
|
||||
case error::operation_canceled:
|
||||
return "Operation canceled";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
@@ -216,9 +205,9 @@ namespace websocketpp {
|
||||
|
||||
class exception : public std::exception {
|
||||
public:
|
||||
exception(std::string const & msg, error::value p_code = error::general)
|
||||
: m_msg(msg), m_code(p_code) {}
|
||||
|
||||
exception(std::string const & msg,
|
||||
error::value code = error::general)
|
||||
: m_msg(msg),m_code(code) {}
|
||||
~exception() throw() {}
|
||||
|
||||
virtual char const * what() const throw() {
|
||||
|
||||
@@ -28,10 +28,8 @@
|
||||
#ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
|
||||
#define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
|
||||
|
||||
|
||||
#include <websocketpp/common/cpp11.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
#include <websocketpp/common/platforms.hpp>
|
||||
#include <websocketpp/common/system_error.hpp>
|
||||
#include <websocketpp/error.hpp>
|
||||
|
||||
@@ -50,8 +48,8 @@ namespace extensions {
|
||||
/**
|
||||
* ### permessage-deflate interface
|
||||
*
|
||||
* **is_implemented**\n
|
||||
* `bool is_implemented()`\n
|
||||
* **is_implimented**\n
|
||||
* `bool is_implimented()`\n
|
||||
* Returns whether or not the object impliments the extension or not
|
||||
*
|
||||
* **is_enabled**\n
|
||||
@@ -435,7 +433,7 @@ public:
|
||||
* @return Validation error or 0 on success
|
||||
*/
|
||||
lib::error_code validate_offer(http::attribute_list const & response) {
|
||||
return make_error_code(error::general);
|
||||
|
||||
}
|
||||
|
||||
/// Negotiate extension
|
||||
@@ -489,6 +487,7 @@ public:
|
||||
}
|
||||
|
||||
size_t output;
|
||||
int ret;
|
||||
|
||||
m_dstate.avail_out = m_compress_buffer_size;
|
||||
m_dstate.next_in = (unsigned char *)(const_cast<char *>(in.data()));
|
||||
@@ -498,7 +497,7 @@ public:
|
||||
m_dstate.avail_out = m_compress_buffer_size;
|
||||
m_dstate.next_out = m_compress_buffer.get();
|
||||
|
||||
deflate(&m_dstate, Z_SYNC_FLUSH);
|
||||
ret = deflate(&m_dstate, Z_SYNC_FLUSH);
|
||||
|
||||
output = m_compress_buffer_size - m_dstate.avail_out;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace http {
|
||||
static char const header_delimiter[] = "\r\n";
|
||||
|
||||
/// Literal value of the HTTP header separator
|
||||
static char const header_separator[] = ":";
|
||||
static char const header_separator[] = ": ";
|
||||
|
||||
/// Literal value of an empty header
|
||||
static std::string const empty_header = "";
|
||||
@@ -255,7 +255,7 @@ namespace http {
|
||||
case internal_server_error:
|
||||
return "Internal Server Error";
|
||||
case not_implemented:
|
||||
return "Not Implemented";
|
||||
return "Not Implimented";
|
||||
case bad_gateway:
|
||||
return "Bad Gateway";
|
||||
case service_unavailable:
|
||||
|
||||
@@ -147,8 +147,8 @@ inline void parser::process_header(std::string::iterator begin,
|
||||
throw exception("Invalid header line",status_code::bad_request);
|
||||
}
|
||||
|
||||
append_header(strip_lws(std::string(begin,cursor)),
|
||||
strip_lws(std::string(cursor+sizeof(header_separator)-1,end)));
|
||||
append_header(std::string(begin,cursor),
|
||||
std::string(cursor+sizeof(header_separator)-1,end));
|
||||
}
|
||||
|
||||
inline std::string parser::raw_headers() const {
|
||||
|
||||
@@ -38,15 +38,15 @@ namespace http {
|
||||
namespace parser {
|
||||
|
||||
inline bool request::parse_complete(std::istream& s) {
|
||||
std::string req;
|
||||
std::string request;
|
||||
|
||||
// get status line
|
||||
std::getline(s, req);
|
||||
std::getline(s, request);
|
||||
|
||||
if (req[req.size()-1] == '\r') {
|
||||
req.erase(req.end()-1);
|
||||
if (request[request.size()-1] == '\r') {
|
||||
request.erase(request.end()-1);
|
||||
|
||||
std::stringstream ss(req);
|
||||
std::stringstream ss(request);
|
||||
std::string val;
|
||||
|
||||
ss >> val;
|
||||
@@ -127,18 +127,18 @@ inline size_t request::consume(const char *buf, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
begin = end+(sizeof(header_delimiter)-1);
|
||||
begin = end+sizeof(header_delimiter)-1;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string request::raw() {
|
||||
// TODO: validation. Make sure all required fields have been set?
|
||||
std::stringstream ret;
|
||||
std::stringstream raw;
|
||||
|
||||
ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
|
||||
ret << raw_headers() << "\r\n" << m_body;
|
||||
raw << m_method << " " << m_uri << " " << get_version() << "\r\n";
|
||||
raw << raw_headers() << "\r\n" << m_body;
|
||||
|
||||
return ret.str();
|
||||
return raw.str();
|
||||
}
|
||||
|
||||
inline void request::set_method(const std::string& method) {
|
||||
|
||||
@@ -172,15 +172,15 @@ inline size_t response::consume(std::istream & s) {
|
||||
|
||||
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 line;
|
||||
std::string response;
|
||||
|
||||
// get status line
|
||||
std::getline(s, line);
|
||||
std::getline(s, response);
|
||||
|
||||
if (line[line.size()-1] == '\r') {
|
||||
line.erase(line.end()-1);
|
||||
if (response[response.size()-1] == '\r') {
|
||||
response.erase(response.end()-1);
|
||||
|
||||
std::stringstream ss(line);
|
||||
std::stringstream ss(response);
|
||||
std::string str_val;
|
||||
int int_val;
|
||||
char char_val[256];
|
||||
@@ -201,14 +201,14 @@ inline bool response::parse_complete(std::istream& s) {
|
||||
inline std::string response::raw() const {
|
||||
// TODO: validation. Make sure all required fields have been set?
|
||||
|
||||
std::stringstream ret;
|
||||
std::stringstream raw;
|
||||
|
||||
ret << get_version() << " " << m_status_code << " " << m_status_msg;
|
||||
ret << "\r\n" << raw_headers() << "\r\n";
|
||||
raw << get_version() << " " << m_status_code << " " << m_status_msg;
|
||||
raw << "\r\n" << raw_headers() << "\r\n";
|
||||
|
||||
ret << m_body;
|
||||
raw << m_body;
|
||||
|
||||
return ret.str();
|
||||
return raw.str();
|
||||
}
|
||||
|
||||
inline void response::set_status(status_code::value code) {
|
||||
|
||||
@@ -367,16 +367,6 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end,
|
||||
return cursor;
|
||||
}
|
||||
|
||||
inline std::string strip_lws(std::string const & input) {
|
||||
std::string::const_iterator begin = extract_all_lws(input.begin(),input.end());
|
||||
if (begin == input.end()) {
|
||||
return std::string();
|
||||
}
|
||||
std::string::const_reverse_iterator end = extract_all_lws(input.rbegin(),input.rend());
|
||||
|
||||
return std::string(begin,end.base());
|
||||
}
|
||||
|
||||
/// Base HTTP parser
|
||||
/**
|
||||
* Includes methods and data elements common to all types of HTTP messages such
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -40,7 +40,6 @@ endpoint<connection,config>::create_connection() {
|
||||
return connection_ptr();
|
||||
}*/
|
||||
|
||||
//scoped_lock_type guard(m_mutex);
|
||||
// Create a connection on the heap and manage it using a shared pointer
|
||||
connection_ptr con(new connection_type(m_is_server,m_user_agent,m_alog,
|
||||
m_elog, m_rng));
|
||||
@@ -65,19 +64,6 @@ endpoint<connection,config>::create_connection() {
|
||||
con->set_validate_handler(m_validate_handler);
|
||||
con->set_message_handler(m_message_handler);
|
||||
|
||||
if (m_open_handshake_timeout_dur != config::timeout_open_handshake) {
|
||||
con->set_open_handshake_timeout(m_open_handshake_timeout_dur);
|
||||
}
|
||||
if (m_close_handshake_timeout_dur != config::timeout_close_handshake) {
|
||||
con->set_close_handshake_timeout(m_close_handshake_timeout_dur);
|
||||
}
|
||||
if (m_pong_timeout_dur != config::timeout_pong) {
|
||||
con->set_pong_timeout(m_pong_timeout_dur);
|
||||
}
|
||||
if (m_max_message_size != config::max_message_size) {
|
||||
con->set_max_message_size(m_max_message_size);
|
||||
}
|
||||
|
||||
lib::error_code ec;
|
||||
|
||||
ec = transport_type::init(con);
|
||||
@@ -90,7 +76,8 @@ endpoint<connection,config>::create_connection() {
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::interrupt(connection_hdl hdl, lib::error_code & ec)
|
||||
void endpoint<connection,config>::interrupt(connection_hdl hdl,
|
||||
lib::error_code & ec)
|
||||
{
|
||||
connection_ptr con = get_con_from_hdl(hdl,ec);
|
||||
if (ec) {return;}
|
||||
@@ -108,42 +95,8 @@ void endpoint<connection,config>::interrupt(connection_hdl hdl) {
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::pause_reading(connection_hdl hdl, lib::error_code & ec)
|
||||
{
|
||||
connection_ptr con = get_con_from_hdl(hdl,ec);
|
||||
if (ec) {return;}
|
||||
|
||||
ec = con->pause_reading();
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::pause_reading(connection_hdl hdl) {
|
||||
lib::error_code ec;
|
||||
pause_reading(hdl,ec);
|
||||
if (ec) { throw ec; }
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::resume_reading(connection_hdl hdl, lib::error_code & ec)
|
||||
{
|
||||
connection_ptr con = get_con_from_hdl(hdl,ec);
|
||||
if (ec) {return;}
|
||||
|
||||
ec = con->resume_reading();
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::resume_reading(connection_hdl hdl) {
|
||||
lib::error_code ec;
|
||||
resume_reading(hdl,ec);
|
||||
if (ec) { throw ec; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::send(connection_hdl hdl, std::string const & payload,
|
||||
frame::opcode::value op, lib::error_code & ec)
|
||||
void endpoint<connection,config>::send(connection_hdl hdl, std::string const &
|
||||
payload, frame::opcode::value op, lib::error_code & ec)
|
||||
{
|
||||
connection_ptr con = get_con_from_hdl(hdl,ec);
|
||||
if (ec) {return;}
|
||||
@@ -152,8 +105,8 @@ void endpoint<connection,config>::send(connection_hdl hdl, std::string const & p
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::send(connection_hdl hdl, std::string const & payload,
|
||||
frame::opcode::value op)
|
||||
void endpoint<connection,config>::send(connection_hdl hdl, std::string const &
|
||||
payload, frame::opcode::value op)
|
||||
{
|
||||
lib::error_code ec;
|
||||
send(hdl,payload,op,ec);
|
||||
@@ -223,7 +176,8 @@ void endpoint<connection,config>::ping(connection_hdl hdl, std::string const &
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::ping(connection_hdl hdl, std::string const & payload)
|
||||
void endpoint<connection,config>::ping(connection_hdl hdl, std::string const &
|
||||
payload)
|
||||
{
|
||||
lib::error_code ec;
|
||||
ping(hdl,payload,ec);
|
||||
@@ -231,8 +185,8 @@ void endpoint<connection,config>::ping(connection_hdl hdl, std::string const & p
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const & payload,
|
||||
lib::error_code & ec)
|
||||
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const &
|
||||
payload, lib::error_code & ec)
|
||||
{
|
||||
connection_ptr con = get_con_from_hdl(hdl,ec);
|
||||
if (ec) {return;}
|
||||
@@ -240,7 +194,8 @@ void endpoint<connection,config>::pong(connection_hdl hdl, std::string const & p
|
||||
}
|
||||
|
||||
template <typename connection, typename config>
|
||||
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const & payload)
|
||||
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const &
|
||||
payload)
|
||||
{
|
||||
lib::error_code ec;
|
||||
pong(hdl,payload,ec);
|
||||
|
||||
@@ -67,6 +67,30 @@ public:
|
||||
return message_ptr(new message(type::shared_from_this(),op,size));
|
||||
}
|
||||
|
||||
/*message_ptr get_incoming_message() {
|
||||
return get_message();
|
||||
}
|
||||
|
||||
message_ptr get_outgoing_message() {
|
||||
return get_message();
|
||||
}*/
|
||||
|
||||
message_ptr get_incoming_message(frame::opcode::value op, size_t size) {
|
||||
return get_message(op,size);
|
||||
}
|
||||
|
||||
message_ptr get_outgoing_message(frame::opcode::value op, size_t size) {
|
||||
return get_message(op,size);
|
||||
}
|
||||
|
||||
void message_handler_hook(message_ptr msg) {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
void write_handler_hook(message_ptr msg) {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
/// Recycle a message
|
||||
/**
|
||||
* This method shouldn't be called. If it is, return false to indicate an
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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_MESSAGE_BUFFER_FIXED_HPP
|
||||
#define WEBSOCKETPP_MESSAGE_BUFFER_FIXED_HPP
|
||||
|
||||
#include <websocketpp/message_buffer/fixed_message.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
#include <websocketpp/frame.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace message_buffer {
|
||||
namespace fixed {
|
||||
|
||||
class con_msg_manager;
|
||||
class endpoint_msg_manager;
|
||||
|
||||
struct policy {
|
||||
typedef websocketpp::message_buffer::fixed_message message;
|
||||
typedef websocketpp::message_buffer::fixed::con_msg_manager con_msg_manager;
|
||||
typedef websocketpp::message_buffer::fixed::endpoint_msg_manager endpoint_msg_manager;
|
||||
};
|
||||
|
||||
/// A connection message manager that allocates a new message for each
|
||||
/// request.
|
||||
class con_msg_manager : public lib::enable_shared_from_this<con_msg_manager> {
|
||||
public:
|
||||
typedef con_msg_manager type;
|
||||
|
||||
typedef std::shared_ptr<type> ptr;
|
||||
|
||||
typedef policy::message message;
|
||||
typedef policy::message::ptr message_ptr;
|
||||
|
||||
con_msg_manager()
|
||||
: m_incoming_message(new message())
|
||||
, m_incoming_message_busy(false)
|
||||
, m_outgoing_message(new message())
|
||||
, m_outgoing_message_busy(false) {}
|
||||
|
||||
/// Get an empty message buffer
|
||||
/**
|
||||
* @return A shared pointer to an empty new message
|
||||
*/
|
||||
message_ptr get_message() {
|
||||
return message_ptr(new message());
|
||||
}
|
||||
|
||||
message_ptr get_incoming_message(frame::opcode::value op, size_t size) {
|
||||
if (frame::opcode::is_control(op)) {
|
||||
return message_ptr(new message(op, size));
|
||||
} else {
|
||||
if (m_incoming_message_busy) {
|
||||
return message_ptr();
|
||||
} else {
|
||||
m_incoming_message->set_opcode(op);
|
||||
m_incoming_message->reserve(size);
|
||||
return m_incoming_message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message_ptr get_outgoing_message(frame::opcode::value op, size_t size) {
|
||||
if (frame::opcode::is_control(op)) {
|
||||
return message_ptr(new message(op, size));
|
||||
} else {
|
||||
if (m_outgoing_message_busy) {
|
||||
return message_ptr();
|
||||
} else {
|
||||
m_outgoing_message->set_opcode(op);
|
||||
m_outgoing_message->reserve(size);
|
||||
return m_outgoing_message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void message_handler_hook(message_ptr msg) {
|
||||
recycle(msg);
|
||||
}
|
||||
|
||||
void write_handler_hook(message_ptr msg) {
|
||||
// TODO: we shouldn't recycle a message every time it is written. If a
|
||||
// message is queued for writing multiple times... Needs a reference
|
||||
// count in the message to deal with this.
|
||||
recycle(msg);
|
||||
}
|
||||
|
||||
bool recycle(message_ptr msg) {
|
||||
if (msg == m_incoming_message) {
|
||||
m_incoming_message_busy = false;
|
||||
msg->reset();
|
||||
return true;
|
||||
} else if (msg == m_outgoing_message) {
|
||||
m_outgoing_message_busy = false;
|
||||
msg->reset();
|
||||
return true;
|
||||
} else {
|
||||
// not a message we are managing, ignore
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a pointer to a connection message manager
|
||||
/**
|
||||
* @return A pointer to the requested connection message manager.
|
||||
*/
|
||||
ptr get_manager() {
|
||||
return shared_from_this();
|
||||
}
|
||||
private:
|
||||
message_ptr m_incoming_message;
|
||||
bool m_incoming_message_busy;
|
||||
message_ptr m_outgoing_message;
|
||||
bool m_outgoing_message_busy;
|
||||
};
|
||||
|
||||
/// An endpoint message manager that allocates a new manager for each
|
||||
/// connection.
|
||||
class endpoint_msg_manager {
|
||||
public:
|
||||
typedef policy::con_msg_manager::ptr con_msg_man_ptr;
|
||||
|
||||
/// Get a pointer to a connection message manager
|
||||
/**
|
||||
* @return A pointer to the requested connection message manager.
|
||||
*/
|
||||
con_msg_man_ptr get_manager() const {
|
||||
return con_msg_man_ptr(new con_msg_manager());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fixed
|
||||
} // namespace message_buffer
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_MESSAGE_BUFFER_FIXED_HPP
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* 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_MESSAGE_BUFFER_FIXED_MESSAGE_HPP
|
||||
#define WEBSOCKETPP_MESSAGE_BUFFER_FIXED_MESSAGE_HPP
|
||||
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
#include <websocketpp/frame.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace message_buffer {
|
||||
|
||||
/* # message:
|
||||
* object that stores a message while it is being sent or received. Contains
|
||||
* the message payload itself, the message header, the extension data, and the
|
||||
* opcode.
|
||||
*
|
||||
* # connection_message_manager:
|
||||
* An object that manages all of the message_buffers associated with a given
|
||||
* connection. Impliments the get_message_buffer(size) method that returns
|
||||
* a message buffer at least size bytes long.
|
||||
*
|
||||
* Message buffers are reference counted with shared ownership semantics. Once
|
||||
* requested from the manager the requester and it's associated downstream code
|
||||
* may keep a pointer to the message indefinitely at a cost of extra resource
|
||||
* usage. Once the reference count drops to the point where the manager is the
|
||||
* only reference the messages is recycled using whatever method is implemented
|
||||
* in the manager.
|
||||
*
|
||||
* # endpoint_message_manager:
|
||||
* An object that manages connection_message_managers. Impliments the
|
||||
* get_message_manager() method. This is used once by each connection to
|
||||
* request the message manager that they are supposed to use to manage message
|
||||
* buffers for their own use.
|
||||
*
|
||||
* TYPES OF CONNECTION_MESSAGE_MANAGERS
|
||||
* - allocate a message with the exact size every time one is requested
|
||||
* - maintain a pool of pre-allocated messages and return one when needed.
|
||||
* Recycle previously used messages back into the pool
|
||||
*
|
||||
* TYPES OF ENDPOINT_MESSAGE_MANAGERS
|
||||
* - allocate a new connection manager for each connection. Message pools
|
||||
* become connection specific. This increases memory usage but improves
|
||||
* concurrency.
|
||||
* - allocate a single connection manager and share a pointer to it with all
|
||||
* connections created by this endpoint. The message pool will be shared
|
||||
* among all connections, improving memory usage and performance at the cost
|
||||
* of reduced concurrency
|
||||
*/
|
||||
|
||||
|
||||
/// Represents a buffer for a single WebSocket message.
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class fixed_message {
|
||||
public:
|
||||
typedef lib::shared_ptr<fixed_message> ptr;
|
||||
|
||||
/// Construct an empty message
|
||||
/**
|
||||
* Construct an empty message
|
||||
*/
|
||||
fixed_message()
|
||||
: m_prepared(false)
|
||||
, m_fin(true)
|
||||
, m_terminal(false)
|
||||
, m_compressed(false) {}
|
||||
|
||||
/// Construct a message and fill in some values
|
||||
/**
|
||||
*
|
||||
*/
|
||||
fixed_message(frame::opcode::value op, size_t size = 128)
|
||||
: m_opcode(op)
|
||||
, m_prepared(false)
|
||||
, m_fin(true)
|
||||
, m_terminal(false)
|
||||
, m_compressed(false)
|
||||
{
|
||||
m_payload.reserve(size);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_payload.clear();
|
||||
m_prepared = false;
|
||||
m_fin = true;
|
||||
m_terminal = false;
|
||||
m_compressed = false;
|
||||
}
|
||||
|
||||
/// Return whether or not the message has been prepared for sending
|
||||
/**
|
||||
* The prepared flag indicates that the message has been prepared by a
|
||||
* websocket protocol processor and is ready to be written to the wire.
|
||||
*
|
||||
* @return whether or not the message has been prepared for sending
|
||||
*/
|
||||
bool get_prepared() const {
|
||||
return m_prepared;
|
||||
}
|
||||
|
||||
/// Set or clear the flag that indicates that the message has been prepared
|
||||
/**
|
||||
* This flag should not be set by end user code without a very good reason.
|
||||
*
|
||||
* @param value The value to set the prepared flag to
|
||||
*/
|
||||
void set_prepared(bool value) {
|
||||
m_prepared = value;
|
||||
}
|
||||
|
||||
/// Return whether or not the message is flagged as compressed
|
||||
/**
|
||||
* @return whether or not the message is/should be compressed
|
||||
*/
|
||||
bool get_compressed() const {
|
||||
return m_compressed;
|
||||
}
|
||||
|
||||
/// Set or clear the compression flag
|
||||
/**
|
||||
* The compression flag is used to indicate whether or not the message is
|
||||
* or should be compressed. Compression is not guaranteed. Both endpoints
|
||||
* must support a compression extension and the connection must have had
|
||||
* that extension negotiated in its handshake.
|
||||
*
|
||||
* @param value The value to set the compressed flag to
|
||||
*/
|
||||
void set_compressed(bool value) {
|
||||
m_compressed = value;
|
||||
}
|
||||
|
||||
/// Get whether or not the message is terminal
|
||||
/**
|
||||
* Messages can be flagged as terminal, which results in the connection
|
||||
* being close after they are written rather than the implementation going
|
||||
* on to the next message in the queue. This is typically used internally
|
||||
* for close messages only.
|
||||
*
|
||||
* @return Whether or not this message is marked terminal
|
||||
*/
|
||||
bool get_terminal() const {
|
||||
return m_terminal;
|
||||
}
|
||||
|
||||
/// Set the terminal flag
|
||||
/**
|
||||
* This flag should not be set by end user code without a very good reason.
|
||||
*
|
||||
* @see get_terminal()
|
||||
*
|
||||
* @param value The value to set the terminal flag to.
|
||||
*/
|
||||
void set_terminal(bool value) {
|
||||
m_terminal = value;
|
||||
}
|
||||
/// Read the fin bit
|
||||
/**
|
||||
* A message with the fin bit set will be sent as the last message of its
|
||||
* sequence. A message with the fin bit cleared will require subsequent
|
||||
* frames of opcode continuation until one of them has the fin bit set.
|
||||
*
|
||||
* The remote end likely will not deliver any bytes until the frame with the fin
|
||||
* bit set has been received.
|
||||
*
|
||||
* @return Whether or not the fin bit is set
|
||||
*/
|
||||
bool get_fin() const {
|
||||
return m_fin;
|
||||
}
|
||||
|
||||
/// Set the fin bit
|
||||
/**
|
||||
* @see get_fin for a more detailed explaination of the fin bit
|
||||
*
|
||||
* @param value The value to set the fin bit to.
|
||||
*/
|
||||
void set_fin(bool value) {
|
||||
m_fin = value;
|
||||
}
|
||||
|
||||
/// Return the message opcode
|
||||
frame::opcode::value get_opcode() const {
|
||||
return m_opcode;
|
||||
}
|
||||
|
||||
/// Set the opcode
|
||||
void set_opcode(frame::opcode::value op) {
|
||||
m_opcode = op;
|
||||
}
|
||||
|
||||
/// Return the prepared frame header
|
||||
/**
|
||||
* This value is typically set by a websocket protocol processor
|
||||
* and shouldn't be tampered with.
|
||||
*/
|
||||
std::string const & get_header() const {
|
||||
return m_header;
|
||||
}
|
||||
|
||||
/// Set prepared frame header
|
||||
/**
|
||||
* Under normal circumstances this should not be called by end users
|
||||
*
|
||||
* @param header A string to set the header to.
|
||||
*/
|
||||
void set_header(std::string const & header) {
|
||||
m_header = header;
|
||||
}
|
||||
|
||||
std::string const & get_extension_data() const {
|
||||
return m_extension_data;
|
||||
}
|
||||
|
||||
/// Get a reference to the payload string
|
||||
/**
|
||||
* @return A const reference to the message's payload string
|
||||
*/
|
||||
std::string const & get_payload() const {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
/// Get a non-const reference to the payload string
|
||||
/**
|
||||
* @return A reference to the message's payload string
|
||||
*/
|
||||
std::string & get_raw_payload() {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
/// Set payload data
|
||||
/**
|
||||
* Set the message buffer's payload to the given value.
|
||||
*
|
||||
* @param payload A string to set the payload to.
|
||||
*/
|
||||
void set_payload(std::string const & payload) {
|
||||
m_payload = payload;
|
||||
}
|
||||
|
||||
/// Set payload data
|
||||
/**
|
||||
* Set the message buffer's payload to the given value.
|
||||
*
|
||||
* @param payload A pointer to a data array to set to.
|
||||
* @param len The length of new payload in bytes.
|
||||
*/
|
||||
void set_payload(void const * payload, size_t len) {
|
||||
m_payload.reserve(len);
|
||||
char const * pl = static_cast<char const *>(payload);
|
||||
m_payload.assign(pl, pl + len);
|
||||
}
|
||||
|
||||
/// Append payload data
|
||||
/**
|
||||
* Append data to the message buffer's payload.
|
||||
*
|
||||
* @param payload A string containing the data array to append.
|
||||
*/
|
||||
void append_payload(std::string const & payload) {
|
||||
m_payload.append(payload);
|
||||
}
|
||||
|
||||
/// Append payload data
|
||||
/**
|
||||
* Append data to the message buffer's payload.
|
||||
*
|
||||
* @param payload A pointer to a data array to append
|
||||
* @param len The length of payload in bytes
|
||||
*/
|
||||
void append_payload(void const * payload, size_t len) {
|
||||
m_payload.reserve(m_payload.size()+len);
|
||||
m_payload.append(static_cast<char const *>(payload),len);
|
||||
}
|
||||
|
||||
void reserve(size_t size) {
|
||||
m_payload.reserve(size);
|
||||
}
|
||||
private:
|
||||
std::string m_header;
|
||||
std::string m_extension_data;
|
||||
std::string m_payload;
|
||||
frame::opcode::value m_opcode;
|
||||
bool m_prepared;
|
||||
bool m_fin;
|
||||
bool m_terminal;
|
||||
bool m_compressed;
|
||||
};
|
||||
|
||||
} // namespace message_buffer
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_MESSAGE_BUFFER_FIXED_MESSAGE_HPP
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <websocketpp/utf8_validator.hpp>
|
||||
#include <websocketpp/common/network.hpp>
|
||||
#include <websocketpp/common/md5.hpp>
|
||||
#include <websocketpp/common/platforms.hpp>
|
||||
|
||||
#include <websocketpp/processors/processor.hpp>
|
||||
|
||||
@@ -58,8 +57,8 @@ public:
|
||||
|
||||
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
|
||||
|
||||
explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager)
|
||||
: processor<config>(secure, p_is_server)
|
||||
explicit hybi00(bool secure, bool server, msg_manager_ptr manager)
|
||||
: processor<config>(secure, server)
|
||||
, msg_hdr(0x00)
|
||||
, msg_ftr(0xff)
|
||||
, m_state(HEADER)
|
||||
@@ -218,7 +217,7 @@ public:
|
||||
if (m_state == HEADER) {
|
||||
if (buf[p] == msg_hdr) {
|
||||
p++;
|
||||
m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1);
|
||||
m_msg_ptr = m_msg_manager->get_incoming_message(frame::opcode::text,1);
|
||||
|
||||
if (!m_msg_ptr) {
|
||||
ec = make_error_code(websocketpp::error::no_incoming_buffers);
|
||||
|
||||
@@ -45,8 +45,9 @@ public:
|
||||
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
|
||||
typedef typename config::rng_type rng_type;
|
||||
|
||||
explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
|
||||
: hybi08<config>(secure, p_is_server, manager, rng) {}
|
||||
explicit hybi07(bool secure, bool server, msg_manager_ptr manager,
|
||||
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,
|
||||
|
||||
@@ -46,8 +46,9 @@ public:
|
||||
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
|
||||
typedef typename config::rng_type rng_type;
|
||||
|
||||
explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
|
||||
: hybi13<config>(secure, p_is_server, manager, rng) {}
|
||||
explicit hybi08(bool secure, bool server, msg_manager_ptr manager,
|
||||
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,
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <websocketpp/frame.hpp>
|
||||
#include <websocketpp/utf8_validator.hpp>
|
||||
#include <websocketpp/common/network.hpp>
|
||||
#include <websocketpp/common/platforms.hpp>
|
||||
#include <websocketpp/http/constants.hpp>
|
||||
|
||||
#include <websocketpp/processors/processor.hpp>
|
||||
@@ -68,8 +67,9 @@ public:
|
||||
|
||||
typedef std::pair<lib::error_code,std::string> err_str_pair;
|
||||
|
||||
explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
|
||||
: processor<config>(secure, p_is_server)
|
||||
explicit hybi13(bool secure, bool server, msg_manager_ptr manager,
|
||||
rng_type& rng)
|
||||
: processor<config>(secure,server)
|
||||
, m_msg_manager(manager)
|
||||
, m_rng(rng)
|
||||
{
|
||||
@@ -331,14 +331,6 @@ public:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (base::m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "Received frame basic header: "
|
||||
<< utility::to_hex(&m_basic_header.b0,1)
|
||||
<< utility::to_hex(&m_basic_header.b1,1);
|
||||
base::m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
ec = this->validate_incoming_basic_header(
|
||||
m_basic_header, base::m_server, !m_data_msg.msg_ptr
|
||||
);
|
||||
@@ -356,17 +348,6 @@ public:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (base::m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "Received frame full header: "
|
||||
<< utility::to_hex(&m_basic_header.b0,1)
|
||||
<< utility::to_hex(&m_basic_header.b1,1)
|
||||
<< utility::to_hex(m_extended_header.bytes,
|
||||
frame::get_header_len(m_basic_header) -
|
||||
frame::BASIC_HEADER_LENGTH);
|
||||
base::m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
ec = validate_incoming_extended_header(m_basic_header,m_extended_header);
|
||||
if (ec){break;}
|
||||
|
||||
@@ -381,32 +362,18 @@ public:
|
||||
|
||||
if (frame::opcode::is_control(op)) {
|
||||
m_control_msg = msg_metadata(
|
||||
m_msg_manager->get_message(op,m_bytes_needed),
|
||||
m_msg_manager->get_incoming_message(op,m_bytes_needed),
|
||||
frame::get_masking_key(m_basic_header,m_extended_header)
|
||||
);
|
||||
|
||||
m_current_msg = &m_control_msg;
|
||||
} else {
|
||||
if (!m_data_msg.msg_ptr) {
|
||||
if (m_bytes_needed > base::m_max_message_size) {
|
||||
ec = make_error_code(error::message_too_big);
|
||||
break;
|
||||
}
|
||||
|
||||
m_data_msg = msg_metadata(
|
||||
m_msg_manager->get_message(op,m_bytes_needed),
|
||||
m_msg_manager->get_incoming_message(op,m_bytes_needed),
|
||||
frame::get_masking_key(m_basic_header,m_extended_header)
|
||||
);
|
||||
} else {
|
||||
// Fetch the underlying payload buffer from the data message we
|
||||
// are writing into.
|
||||
std::string & out = m_data_msg.msg_ptr->get_raw_payload();
|
||||
|
||||
if (out.size() + m_bytes_needed > base::m_max_message_size) {
|
||||
ec = make_error_code(error::message_too_big);
|
||||
break;
|
||||
}
|
||||
|
||||
// Each frame starts a new masking key. All other state
|
||||
// remains between frames.
|
||||
m_data_msg.prepared_key = prepare_masking_key(
|
||||
@@ -415,8 +382,7 @@ public:
|
||||
m_extended_header
|
||||
)
|
||||
);
|
||||
|
||||
out.reserve(out.size() + m_bytes_needed);
|
||||
// TODO: reserve space in the existing message for the new bytes
|
||||
}
|
||||
m_current_msg = &m_data_msg;
|
||||
}
|
||||
|
||||
@@ -31,12 +31,9 @@
|
||||
#include <websocketpp/processors/base.hpp>
|
||||
#include <websocketpp/common/system_error.hpp>
|
||||
|
||||
|
||||
#include <websocketpp/close.hpp>
|
||||
#include <websocketpp/common/cpp11.hpp>
|
||||
#include <websocketpp/utilities.hpp>
|
||||
#include <websocketpp/uri.hpp>
|
||||
#include <websocketpp/logger/levels.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -162,71 +159,15 @@ public:
|
||||
typedef typename config::message_type::ptr message_ptr;
|
||||
typedef std::pair<lib::error_code,std::string> err_str_pair;
|
||||
|
||||
/// Type of this processor's error logging policy
|
||||
typedef typename config::elog_type elog_type;
|
||||
/// Type of this processor's access logging policy
|
||||
typedef typename config::alog_type alog_type;
|
||||
|
||||
explicit processor(bool secure, bool p_is_server)
|
||||
explicit processor(bool secure, bool server)
|
||||
: m_secure(secure)
|
||||
, m_server(p_is_server)
|
||||
, m_max_message_size(config::max_message_size)
|
||||
, m_elog(_WEBSOCKETPP_NULLPTR_TOKEN_)
|
||||
, m_alog(_WEBSOCKETPP_NULLPTR_TOKEN_)
|
||||
{}
|
||||
, m_server(server) {}
|
||||
|
||||
virtual ~processor() {}
|
||||
|
||||
/// Get the protocol version of this processor
|
||||
virtual int get_version() const = 0;
|
||||
|
||||
/// Initialize logging
|
||||
/**
|
||||
* The loggers are managed by the owner of the processor. As such, the
|
||||
* processor doesn't have direct access to them. This method is called by
|
||||
* the owner to allow shared logging from the processor component. These are
|
||||
* raw pointers to the logging types specified in the config. It is the
|
||||
* responsibility of the owning object to ensure that they remain in scope
|
||||
* until the processor goes out of scope.
|
||||
*
|
||||
* TODO: better way of handling non-initialized logging?
|
||||
*
|
||||
* @param a A pointer to the access logger to use.
|
||||
* @param e A pointer to the error logger to use.
|
||||
*/
|
||||
void init_logging(alog_type * a, elog_type * e) {
|
||||
m_elog = e;
|
||||
m_alog = a;
|
||||
}
|
||||
|
||||
/// Get maximum message size
|
||||
/**
|
||||
* Get maximum message size. Maximum message size determines the point at which the
|
||||
* processor will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is retrieved from the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
size_t get_max_message_size() const {
|
||||
return m_max_message_size;
|
||||
}
|
||||
|
||||
/// Set maximum message size
|
||||
/**
|
||||
* Set maximum message size. Maximum message size determines the point at which the
|
||||
* processor will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is retrieved from the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param new_value The value to set as the maximum message size.
|
||||
*/
|
||||
void set_max_message_size(size_t new_value) {
|
||||
m_max_message_size = new_value;
|
||||
}
|
||||
|
||||
/// Returns whether or not the permessage_compress extension is implemented
|
||||
/**
|
||||
* Compile time flag that indicates whether this processor has implemented
|
||||
@@ -255,7 +196,8 @@ public:
|
||||
* @return A status code, 0 on success, non-zero for specific sorts of
|
||||
* failure
|
||||
*/
|
||||
virtual lib::error_code validate_handshake(request_type const & request) const = 0;
|
||||
virtual lib::error_code validate_handshake(request_type const & request)
|
||||
const = 0;
|
||||
|
||||
/// Calculate the appropriate response for this websocket request
|
||||
/**
|
||||
@@ -294,7 +236,8 @@ public:
|
||||
virtual std::string get_raw(response_type const & request) const = 0;
|
||||
|
||||
/// Return the value of the header containing the CORS origin.
|
||||
virtual std::string const & get_origin(request_type const & request) const = 0;
|
||||
virtual std::string const & get_origin(request_type const & request)
|
||||
const = 0;
|
||||
|
||||
/// Extracts requested subprotocols from a handshake request
|
||||
/**
|
||||
@@ -367,7 +310,8 @@ public:
|
||||
* Performs validation, masking, compression, etc. will return an error if
|
||||
* there was an error, otherwise msg will be ready to be written
|
||||
*/
|
||||
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0;
|
||||
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
|
||||
= 0;
|
||||
|
||||
/// Prepare a ping frame
|
||||
/**
|
||||
@@ -380,8 +324,8 @@ public:
|
||||
*
|
||||
* @return Status code, zero on success, non-zero on failure
|
||||
*/
|
||||
virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const
|
||||
= 0;
|
||||
virtual lib::error_code prepare_ping(std::string const & in,
|
||||
message_ptr out) const = 0;
|
||||
|
||||
/// Prepare a pong frame
|
||||
/**
|
||||
@@ -394,8 +338,8 @@ public:
|
||||
*
|
||||
* @return Status code, zero on success, non-zero on failure
|
||||
*/
|
||||
virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const
|
||||
= 0;
|
||||
virtual lib::error_code prepare_pong(std::string const & in,
|
||||
message_ptr out) const = 0;
|
||||
|
||||
/// Prepare a close frame
|
||||
/**
|
||||
@@ -417,9 +361,6 @@ public:
|
||||
protected:
|
||||
bool const m_secure;
|
||||
bool const m_server;
|
||||
size_t m_max_message_size;
|
||||
elog_type* m_elog;
|
||||
alog_type* m_alog;
|
||||
};
|
||||
|
||||
} // namespace processor
|
||||
|
||||
@@ -64,101 +64,52 @@ public:
|
||||
/// Type of the endpoint component of this server
|
||||
typedef endpoint<connection_type,config> endpoint_type;
|
||||
|
||||
|
||||
// TODO: clean up these types
|
||||
|
||||
explicit server() : endpoint_type(true)
|
||||
{
|
||||
endpoint_type::m_alog.write(log::alevel::devel, "server constructor");
|
||||
endpoint_type::m_alog.write(log::alevel::devel,
|
||||
"server constructor");
|
||||
}
|
||||
|
||||
/// Create and initialize a new connection
|
||||
/**
|
||||
* The connection will be initialized and ready to begin. Call its start()
|
||||
* method to begin the processing loop.
|
||||
*
|
||||
* Note: The connection must either be started or terminated using
|
||||
* connection::terminate in order to avoid memory leaks.
|
||||
*
|
||||
* @return A pointer to the new connection.
|
||||
*/
|
||||
// return an initialized connection_ptr. Call start() on this object to
|
||||
// begin the processing loop.
|
||||
connection_ptr get_connection() {
|
||||
return endpoint_type::create_connection();
|
||||
connection_ptr con = endpoint_type::create_connection();
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
/// Starts the server's async connection acceptance loop (exception free)
|
||||
/**
|
||||
* Initiates the server connection acceptance loop. Must be called after
|
||||
* listen. This method will have no effect until the underlying io_service
|
||||
* starts running. It may be called after the io_service is already running.
|
||||
*
|
||||
* Refer to documentation for the transport policy you are using for
|
||||
* instructions on how to stop this acceptance loop.
|
||||
*
|
||||
* @param [out] ec A status code indicating an error, if any.
|
||||
*/
|
||||
void start_accept(lib::error_code & ec) {
|
||||
if (!transport_type::is_listening()) {
|
||||
ec = error::make_error_code(error::async_accept_not_listening);
|
||||
return;
|
||||
}
|
||||
|
||||
ec = lib::error_code();
|
||||
// Starts the server's async connection acceptance loop.
|
||||
void start_accept() {
|
||||
connection_ptr con = get_connection();
|
||||
|
||||
|
||||
transport_type::async_accept(
|
||||
lib::static_pointer_cast<transport_con_type>(con),
|
||||
lib::bind(&type::handle_accept,this,con,lib::placeholders::_1),
|
||||
ec
|
||||
lib::bind(
|
||||
&type::handle_accept,
|
||||
this,
|
||||
con,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
|
||||
if (ec && con) {
|
||||
// If the connection was constructed but the accept failed,
|
||||
// terminate the connection to prevent memory leaks
|
||||
con->terminate(lib::error_code());
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the server's async connection acceptance loop
|
||||
/**
|
||||
* Initiates the server connection acceptance loop. Must be called after
|
||||
* listen. This method will have no effect until the underlying io_service
|
||||
* starts running. It may be called after the io_service is already running.
|
||||
*
|
||||
* Refer to documentation for the transport policy you are using for
|
||||
* instructions on how to stop this acceptance loop.
|
||||
*/
|
||||
void start_accept() {
|
||||
lib::error_code ec;
|
||||
start_accept(ec);
|
||||
if (ec) {
|
||||
throw ec;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler callback for start_accept
|
||||
void handle_accept(connection_ptr con, lib::error_code const & ec) {
|
||||
void handle_accept(connection_ptr con, const lib::error_code& ec) {
|
||||
if (ec) {
|
||||
con->terminate(ec);
|
||||
|
||||
if (ec == error::operation_canceled) {
|
||||
endpoint_type::m_elog.write(log::elevel::info,
|
||||
"handle_accept error: "+ec.message());
|
||||
} else {
|
||||
endpoint_type::m_elog.write(log::elevel::rerror,
|
||||
"handle_accept error: "+ec.message());
|
||||
}
|
||||
endpoint_type::m_elog.write(log::elevel::rerror,
|
||||
"handle_accept error: "+ec.message());
|
||||
} else {
|
||||
con->start();
|
||||
}
|
||||
|
||||
lib::error_code start_ec;
|
||||
start_accept(start_ec);
|
||||
if (start_ec == error::async_accept_not_listening) {
|
||||
endpoint_type::m_elog.write(log::elevel::info,
|
||||
"Stopping acceptance of new connections because the underlying transport is no longer listening.");
|
||||
} else if (start_ec) {
|
||||
endpoint_type::m_elog.write(log::elevel::rerror,
|
||||
"Restarting async_accept loop failed: "+ec.message());
|
||||
}
|
||||
// TODO: are there cases where we should terminate this loop?
|
||||
start_accept();
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
|
||||
#define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
|
||||
|
||||
#include <websocketpp/common/cpp11.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
#include <websocketpp/common/functional.hpp>
|
||||
#include <websocketpp/common/connection_hdl.hpp>
|
||||
@@ -124,7 +123,7 @@ public:
|
||||
m_tcp_pre_init_handler = h;
|
||||
}
|
||||
|
||||
/// Sets the tcp pre init handler (deprecated)
|
||||
/// Sets the tcp pre init handler (deprecated)
|
||||
/**
|
||||
* The tcp pre init handler is called after the raw tcp connection has been
|
||||
* established but before any additional wrappers (proxy connects, TLS
|
||||
@@ -408,7 +407,7 @@ protected:
|
||||
/// Finish constructing the transport
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
* boost::asio components to the io_service.
|
||||
* boost::asio components to the io_service
|
||||
*
|
||||
* @param io_service A pointer to the io_service to register with this
|
||||
* connection
|
||||
@@ -416,37 +415,35 @@ protected:
|
||||
* @return Status code for the success or failure of the initialization
|
||||
*/
|
||||
lib::error_code 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;
|
||||
|
||||
if (config::enable_multithreading) {
|
||||
m_strand.reset(new boost::asio::strand(*io_service));
|
||||
|
||||
m_async_read_handler = m_strand->wrap(lib::bind(
|
||||
&type::handle_async_read, get_shared(),lib::placeholders::_1,
|
||||
lib::placeholders::_2));
|
||||
&type::handle_async_read, get_shared(),
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
));
|
||||
|
||||
m_async_write_handler = m_strand->wrap(lib::bind(
|
||||
&type::handle_async_write, get_shared(),lib::placeholders::_1,
|
||||
lib::placeholders::_2));
|
||||
&type::handle_async_write, get_shared(),
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
));
|
||||
} else {
|
||||
m_async_read_handler = lib::bind(&type::handle_async_read,
|
||||
get_shared(), lib::placeholders::_1, lib::placeholders::_2);
|
||||
// TODO: goal: not have this line here
|
||||
//m_strand.reset(new boost::asio::strand(*io_service));
|
||||
|
||||
m_async_read_handler = lib::bind(
|
||||
&type::handle_async_read, get_shared(),
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
);
|
||||
|
||||
m_async_write_handler = lib::bind(&type::handle_async_write,
|
||||
get_shared(), lib::placeholders::_1, lib::placeholders::_2);
|
||||
}
|
||||
|
||||
lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
|
||||
m_is_server);
|
||||
|
||||
if (ec) {
|
||||
// reset the handlers to break the circular reference:
|
||||
// this->handler->this
|
||||
m_async_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
m_async_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
}
|
||||
|
||||
return ec;
|
||||
return socket_con_type::init_asio(io_service, m_strand, m_is_server);
|
||||
}
|
||||
|
||||
void handle_pre_init(lib::error_code const & ec) {
|
||||
@@ -500,7 +497,7 @@ protected:
|
||||
}
|
||||
|
||||
void handle_post_init_timeout(timer_ptr post_timer, init_handler callback,
|
||||
lib::error_code const & ec)
|
||||
const lib::error_code& ec)
|
||||
{
|
||||
lib::error_code ret_ec;
|
||||
|
||||
@@ -526,8 +523,8 @@ protected:
|
||||
callback(ret_ec);
|
||||
}
|
||||
|
||||
void handle_post_init(timer_ptr post_timer, init_handler callback,
|
||||
lib::error_code const & ec)
|
||||
void handle_post_init(timer_ptr post_timer, init_handler callback, const
|
||||
lib::error_code& ec)
|
||||
{
|
||||
if (ec == transport::error::operation_aborted ||
|
||||
post_timer->expires_from_now().is_negative())
|
||||
@@ -542,7 +539,7 @@ protected:
|
||||
m_alog.write(log::alevel::devel,"asio connection handle_post_init");
|
||||
}
|
||||
|
||||
if (m_tcp_post_init_handler) {
|
||||
if (m_tcp_post_init_handler) {
|
||||
m_tcp_post_init_handler(m_connection_hdl);
|
||||
}
|
||||
|
||||
@@ -624,8 +621,7 @@ protected:
|
||||
boost::system::error_code const & ec)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"asio connection handle_proxy_write");
|
||||
m_alog.write(log::alevel::devel,"asio connection handle_proxy_write");
|
||||
}
|
||||
|
||||
m_bufs.clear();
|
||||
@@ -692,8 +688,7 @@ protected:
|
||||
boost::system::error_code const & ec, size_t bytes_transferred)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"asio connection handle_proxy_read");
|
||||
m_alog.write(log::alevel::devel,"asio connection handle_proxy_read");
|
||||
}
|
||||
|
||||
// Timer expired or the operation was aborted for some reason.
|
||||
@@ -779,13 +774,6 @@ protected:
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
if (!m_async_read_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"async_read_at_least called after async_shutdown");
|
||||
handler(make_error_code(transport::error::action_after_shutdown),0);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: safety vs speed ?
|
||||
// maybe move into an if devel block
|
||||
/*if (num_bytes > len) {
|
||||
@@ -798,86 +786,62 @@ protected:
|
||||
|
||||
m_read_handler = handler;
|
||||
|
||||
if (!m_read_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"asio con async_read_at_least called with bad handler");
|
||||
}
|
||||
|
||||
boost::asio::async_read(
|
||||
socket_con_type::get_socket(),
|
||||
boost::asio::buffer(buf,len),
|
||||
boost::asio::transfer_at_least(num_bytes),
|
||||
make_custom_alloc_handler(
|
||||
m_read_handler_allocator,
|
||||
m_async_read_handler
|
||||
)
|
||||
/*m_strand->wrap(lib::bind(
|
||||
&type::handle_async_read,
|
||||
get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1,
|
||||
lib::placeholders::_2
|
||||
))*/
|
||||
make_custom_alloc_handler(m_read_handler_allocator,m_async_read_handler)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_async_read(boost::system::error_code const & ec,
|
||||
void handle_async_read(const boost::system::error_code& ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
m_alog.write(log::alevel::devel, "asio con handle_async_read");
|
||||
if (!ec) {
|
||||
m_read_handler(lib::error_code(), bytes_transferred);
|
||||
return;
|
||||
}
|
||||
|
||||
// translate boost error codes into more lib::error_codes
|
||||
lib::error_code tec;
|
||||
if (ec == boost::asio::error::eof) {
|
||||
tec = make_error_code(transport::error::eof);
|
||||
} else if (ec) {
|
||||
// We don't know much more about the error at this point. As our
|
||||
// socket/security policy if it knows more:
|
||||
tec = socket_con_type::translate_ec(ec);
|
||||
|
||||
if (tec == transport::error::tls_error ||
|
||||
tec == transport::error::pass_through)
|
||||
{
|
||||
// These are aggregate/catch all errors. Log some human readable
|
||||
// information to the info channel to give library users some
|
||||
// more details about why the upstream method may have failed.
|
||||
log_err(log::elevel::info,"asio async_read_at_least",ec);
|
||||
}
|
||||
}
|
||||
if (m_read_handler) {
|
||||
m_read_handler(tec,bytes_transferred);
|
||||
// TODO: why does this line break things?
|
||||
//m_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
m_read_handler(make_error_code(transport::error::eof),
|
||||
bytes_transferred);
|
||||
} else if (ec.value() == 335544539) {
|
||||
m_read_handler(make_error_code(transport::error::tls_short_read),
|
||||
bytes_transferred);
|
||||
} else {
|
||||
// This can happen in cases where the connection is terminated while
|
||||
// the transport is waiting on a read.
|
||||
m_alog.write(log::alevel::devel,
|
||||
"handle_async_read called with null read handler");
|
||||
log_err(log::elevel::info,"asio async_read_at_least",ec);
|
||||
m_read_handler(make_error_code(transport::error::pass_through),
|
||||
bytes_transferred);
|
||||
}
|
||||
}
|
||||
|
||||
void async_write(const char* buf, size_t len, write_handler handler) {
|
||||
if (!m_async_write_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"async_write (single) called after async_shutdown");
|
||||
handler(make_error_code(transport::error::action_after_shutdown));
|
||||
return;
|
||||
}
|
||||
|
||||
m_bufs.push_back(boost::asio::buffer(buf,len));
|
||||
m_bufs.push_back(boost::asio::buffer(buf,len));
|
||||
|
||||
m_write_handler = handler;
|
||||
|
||||
boost::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
m_async_write_handler
|
||||
)
|
||||
/*m_strand->wrap(lib::bind(
|
||||
&type::handle_async_write,
|
||||
get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1
|
||||
))*/
|
||||
make_custom_alloc_handler(m_write_handler_allocator,m_async_write_handler)
|
||||
);
|
||||
}
|
||||
|
||||
void async_write(const std::vector<buffer>& bufs, write_handler handler) {
|
||||
if (!m_async_write_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"async_write (vector) called after async_shutdown");
|
||||
handler(make_error_code(transport::error::action_after_shutdown));
|
||||
return;
|
||||
}
|
||||
std::vector<buffer>::const_iterator it;
|
||||
|
||||
for (it = bufs.begin(); it != bufs.end(); ++it) {
|
||||
@@ -889,10 +853,13 @@ protected:
|
||||
boost::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
m_async_write_handler
|
||||
)
|
||||
/*m_strand->wrap(lib::bind(
|
||||
&type::handle_async_write,
|
||||
get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1
|
||||
))*/
|
||||
make_custom_alloc_handler(m_write_handler_allocator,m_async_write_handler)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -900,20 +867,11 @@ protected:
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
m_bufs.clear();
|
||||
lib::error_code tec;
|
||||
if (ec) {
|
||||
log_err(log::elevel::info,"asio async_write",ec);
|
||||
tec = make_error_code(transport::error::pass_through);
|
||||
}
|
||||
if (m_write_handler) {
|
||||
m_write_handler(tec);
|
||||
// TODO: why does this line break things?
|
||||
//m_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
m_write_handler(make_error_code(transport::error::pass_through));
|
||||
} else {
|
||||
// This can happen in cases where the connection is terminated while
|
||||
// the transport is waiting on a read.
|
||||
m_alog.write(log::alevel::devel,
|
||||
"handle_async_write called with null write handler");
|
||||
m_write_handler(lib::error_code());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -961,16 +919,6 @@ protected:
|
||||
m_alog.write(log::alevel::devel,"asio connection async_shutdown");
|
||||
}
|
||||
|
||||
// Reset cached handlers now that we won't be reading or writing anymore
|
||||
// These cached handlers store shared pointers to this connection and
|
||||
// will leak the connection if not destroyed.
|
||||
m_async_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
m_async_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
m_init_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
|
||||
m_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
m_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
|
||||
timer_ptr shutdown_timer;
|
||||
shutdown_timer = set_timer(
|
||||
config::timeout_socket_shutdown,
|
||||
@@ -995,7 +943,7 @@ protected:
|
||||
}
|
||||
|
||||
void handle_async_shutdown_timeout(timer_ptr shutdown_timer, init_handler
|
||||
callback, lib::error_code const & ec)
|
||||
callback, const lib::error_code& ec)
|
||||
{
|
||||
lib::error_code ret_ec;
|
||||
|
||||
@@ -1006,7 +954,7 @@ protected:
|
||||
return;
|
||||
}
|
||||
|
||||
log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
|
||||
log_err(log::elevel::devel,"asio handle_async_socket_shutdown",ec);
|
||||
ret_ec = ec;
|
||||
} else {
|
||||
ret_ec = make_error_code(transport::error::timeout);
|
||||
@@ -1019,7 +967,7 @@ protected:
|
||||
}
|
||||
|
||||
void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
|
||||
callback, boost::system::error_code const & ec)
|
||||
callback, const boost::system::error_code & ec)
|
||||
{
|
||||
if (ec == boost::asio::error::operation_aborted ||
|
||||
shutdown_timer->expires_from_now().is_negative())
|
||||
@@ -1030,37 +978,25 @@ protected:
|
||||
|
||||
shutdown_timer->cancel();
|
||||
|
||||
lib::error_code tec;
|
||||
if (ec) {
|
||||
log_err(log::elevel::info,"asio async_shutdown",ec);
|
||||
if (ec == boost::asio::error::not_connected) {
|
||||
// The socket was already closed when we tried to close it. This
|
||||
// happens periodically (usually if a read or write fails
|
||||
// earlier and if it is a real error will be caught at another
|
||||
// level of the stack.
|
||||
callback(lib::error_code());
|
||||
} else {
|
||||
// We don't know anything more about this error, give our
|
||||
// socket/security policy a crack at it.
|
||||
tec = socket_con_type::translate_ec(ec);
|
||||
|
||||
if (tec == transport::error::tls_short_read) {
|
||||
// TLS short read at this point is somewhat expected if both
|
||||
// sides try and end the connection at the same time or if
|
||||
// SSLv2 is being used. In general there is nothing that can
|
||||
// be done here other than a low level development log.
|
||||
} else {
|
||||
// all other errors are effectively pass through errors of
|
||||
// some sort so print some detail on the info channel for
|
||||
// library users to look up if needed.
|
||||
log_err(log::elevel::info,"asio async_shutdown",ec);
|
||||
}
|
||||
callback(make_error_code(transport::error::pass_through));
|
||||
}
|
||||
} else {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"asio con handle_async_shutdown");
|
||||
}
|
||||
|
||||
callback(lib::error_code());
|
||||
}
|
||||
callback(tec);
|
||||
}
|
||||
private:
|
||||
/// Convenience method for logging the code and message for an error_code
|
||||
@@ -1105,7 +1041,7 @@ private:
|
||||
handler_allocator m_write_handler_allocator;
|
||||
|
||||
read_handler m_read_handler;
|
||||
write_handler m_write_handler;
|
||||
write_handler m_write_handler;
|
||||
init_handler m_init_handler;
|
||||
|
||||
async_read_handler m_async_read_handler;
|
||||
|
||||
@@ -84,14 +84,10 @@ public:
|
||||
typedef lib::shared_ptr<boost::asio::ip::tcp::resolver> resolver_ptr;
|
||||
/// Type of timer handle
|
||||
typedef lib::shared_ptr<boost::asio::deadline_timer> timer_ptr;
|
||||
/// Type of a shared pointer to an io_service work object
|
||||
typedef lib::shared_ptr<boost::asio::io_service::work> work_ptr;
|
||||
|
||||
// generate and manage our own io_service
|
||||
explicit endpoint()
|
||||
: m_external_io_service(false)
|
||||
, m_listen_backlog(0)
|
||||
, m_reuse_addr(false)
|
||||
, m_state(UNINITIALIZED)
|
||||
{
|
||||
//std::cout << "transport::asio::endpoint constructor" << std::endl;
|
||||
@@ -123,8 +119,6 @@ public:
|
||||
: m_io_service(src.m_io_service)
|
||||
, m_external_io_service(src.m_external_io_service)
|
||||
, m_acceptor(src.m_acceptor)
|
||||
, m_listen_backlog(boost::asio::socket_base::max_connections)
|
||||
, m_reuse_addr(src.m_reuse_addr)
|
||||
, m_state(src.m_state)
|
||||
{
|
||||
src.m_io_service = NULL;
|
||||
@@ -138,14 +132,11 @@ public:
|
||||
m_io_service = rhs.m_io_service;
|
||||
m_external_io_service = rhs.m_external_io_service;
|
||||
m_acceptor = rhs.m_acceptor;
|
||||
m_listen_backlog = rhs.m_listen_backlog
|
||||
m_reuse_addr = rhs.m_reuse_addr;
|
||||
m_state = rhs.m_state;
|
||||
|
||||
rhs.m_io_service = NULL;
|
||||
rhs.m_external_io_service = false;
|
||||
rhs.m_acceptor = NULL;
|
||||
rhs.m_listen_backlog = boost::asio::socket_base::max_connections;
|
||||
rhs.m_state = UNINITIALIZED;
|
||||
}
|
||||
return *this;
|
||||
@@ -225,90 +216,6 @@ public:
|
||||
m_external_io_service = false;
|
||||
}
|
||||
|
||||
/// Sets the tcp pre init handler
|
||||
/**
|
||||
* The tcp pre init handler is called after the raw tcp connection has been
|
||||
* established but before any additional wrappers (proxy connects, TLS
|
||||
* handshakes, etc) have been performed.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param h The handler to call on tcp pre init.
|
||||
*/
|
||||
void set_tcp_pre_init_handler(tcp_init_handler h) {
|
||||
m_tcp_pre_init_handler = h;
|
||||
}
|
||||
|
||||
/// Sets the tcp pre init handler (deprecated)
|
||||
/**
|
||||
* The tcp pre init handler is called after the raw tcp connection has been
|
||||
* established but before any additional wrappers (proxy connects, TLS
|
||||
* handshakes, etc) have been performed.
|
||||
*
|
||||
* @deprecated Use set_tcp_pre_init_handler instead
|
||||
*
|
||||
* @param h The handler to call on tcp pre init.
|
||||
*/
|
||||
void set_tcp_init_handler(tcp_init_handler h) {
|
||||
set_tcp_pre_init_handler(h);
|
||||
}
|
||||
|
||||
/// Sets the tcp post init handler
|
||||
/**
|
||||
* The tcp post init handler is called after the tcp connection has been
|
||||
* established and all additional wrappers (proxy connects, TLS handshakes,
|
||||
* etc have been performed. This is fired before any bytes are read or any
|
||||
* WebSocket specific handshake logic has been performed.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param h The handler to call on tcp post init.
|
||||
*/
|
||||
void set_tcp_post_init_handler(tcp_init_handler h) {
|
||||
m_tcp_post_init_handler = h;
|
||||
}
|
||||
|
||||
/// Sets the maximum length of the queue of pending connections.
|
||||
/**
|
||||
* Sets the maximum length of the queue of pending connections. Increasing
|
||||
* this will allow WebSocket++ to queue additional incoming connections.
|
||||
* Setting it higher may prevent failed connections at high connection rates
|
||||
* but may cause additional latency.
|
||||
*
|
||||
* For this value to take effect you may need to adjust operating system
|
||||
* settings.
|
||||
*
|
||||
* New values affect future calls to listen only.
|
||||
*
|
||||
* A value of zero will use the operating system default. This is the
|
||||
* default value.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param backlog The maximum length of the queue of pending connections
|
||||
*/
|
||||
void set_listen_backlog(int backlog) {
|
||||
m_listen_backlog = backlog;
|
||||
}
|
||||
|
||||
/// Sets whether or not to use the SO_REUSEADDR flag when opening a listening socket
|
||||
/**
|
||||
* Specifies whether or not to use the SO_REUSEADDR TCP socket option. What this flag
|
||||
* does depends on your operating system. Please consult operating system
|
||||
* documentation for more details.
|
||||
*
|
||||
* New values affect future calls to listen only.
|
||||
*
|
||||
* The default is false.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param value Whether or not to use the SO_REUSEADDR option
|
||||
*/
|
||||
void set_reuse_addr(bool value) {
|
||||
m_reuse_addr = value;
|
||||
}
|
||||
|
||||
/// Retrieve a reference to the endpoint's io_service
|
||||
/**
|
||||
* The io_service may be an internal or external one. This may be used to
|
||||
@@ -324,6 +231,58 @@ public:
|
||||
return *m_io_service;
|
||||
}
|
||||
|
||||
/// Sets the default tcp pre init handler
|
||||
/**
|
||||
* The tcp pre init handler is called after the raw tcp connection has been
|
||||
* established but before any additional wrappers (proxy connects, TLS
|
||||
* handshakes, etc) have been performed.
|
||||
*
|
||||
* @see WebSocket++ handler documentation for more information about
|
||||
* handlers.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param h The handler to call on tcp pre init.
|
||||
*/
|
||||
void set_tcp_pre_init_handler(tcp_init_handler h) {
|
||||
m_tcp_pre_init_handler = h;
|
||||
}
|
||||
|
||||
/// Sets the default tcp pre init handler (deprecated)
|
||||
/**
|
||||
* The tcp pre init handler is called after the raw tcp connection has been
|
||||
* established but before any additional wrappers (proxy connects, TLS
|
||||
* handshakes, etc) have been performed.
|
||||
*
|
||||
* @see WebSocket++ handler documentation for more information about
|
||||
* handlers.
|
||||
*
|
||||
* @deprecated Use set_tcp_pre_init_handler instead
|
||||
*
|
||||
* @param h The handler to call on tcp pre init.
|
||||
*/
|
||||
void set_tcp_init_handler(tcp_init_handler h) {
|
||||
set_tcp_pre_init_handler(h);
|
||||
}
|
||||
|
||||
/// Sets the default tcp pre init handler
|
||||
/**
|
||||
* The tcp post init handler is called after the tcp connection has been
|
||||
* established and all additional wrappers (proxy connects, TLS handshakes,
|
||||
* etc have been performed. This is fired before any bytes are read or any
|
||||
* WebSocket specific handshake logic has been performed.
|
||||
*
|
||||
* @see WebSocket++ handler documentation for more information about
|
||||
* handlers.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param h The handler to call on tcp pre init.
|
||||
*/
|
||||
void set_tcp_post_init_handler(tcp_init_handler h) {
|
||||
m_tcp_post_init_handler = h;
|
||||
}
|
||||
|
||||
/// Set up endpoint for listening manually (exception free)
|
||||
/**
|
||||
* Bind the internal acceptor using the specified settings. The endpoint
|
||||
@@ -344,25 +303,12 @@ public:
|
||||
|
||||
m_alog->write(log::alevel::devel,"asio::listen");
|
||||
|
||||
boost::system::error_code bec;
|
||||
|
||||
m_acceptor->open(ep.protocol(),bec);
|
||||
if (!bec) {
|
||||
m_acceptor->set_option(boost::asio::socket_base::reuse_address(m_reuse_addr),bec);
|
||||
}
|
||||
if (!bec) {
|
||||
m_acceptor->bind(ep,bec);
|
||||
}
|
||||
if (!bec) {
|
||||
m_acceptor->listen(m_listen_backlog,bec);
|
||||
}
|
||||
if (bec) {
|
||||
log_err(log::elevel::info,"asio listen",bec);
|
||||
ec = make_error_code(error::pass_through);
|
||||
} else {
|
||||
m_state = LISTENING;
|
||||
ec = lib::error_code();
|
||||
}
|
||||
m_acceptor->open(ep.protocol());
|
||||
m_acceptor->set_option(boost::asio::socket_base::reuse_address(true));
|
||||
m_acceptor->bind(ep);
|
||||
m_acceptor->listen(8192); // this should be a settable parameter
|
||||
m_state = LISTENING;
|
||||
ec = lib::error_code();
|
||||
}
|
||||
|
||||
/// Set up endpoint for listening manually
|
||||
@@ -548,14 +494,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the endpoint is listening
|
||||
/**
|
||||
* @return Whether or not the endpoint is listening.
|
||||
*/
|
||||
bool is_listening() const {
|
||||
return (m_state == LISTENING);
|
||||
}
|
||||
|
||||
/// wraps the run method of the internal io_service object
|
||||
std::size_t run() {
|
||||
return m_io_service->run();
|
||||
@@ -594,34 +532,6 @@ public:
|
||||
return m_io_service->stopped();
|
||||
}
|
||||
|
||||
/// Marks the endpoint as perpetual, stopping it from exiting when empty
|
||||
/**
|
||||
* Marks the endpoint as perpetual. Perpetual endpoints will not
|
||||
* automatically exit when they run out of connections to process. To stop
|
||||
* a perpetual endpoint call `end_perpetual`.
|
||||
*
|
||||
* An endpoint may be marked perpetual at any time by any thread. It must be
|
||||
* called either before the endpoint has run out of work or before it was
|
||||
* started
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
void start_perpetual() {
|
||||
m_work.reset(new boost::asio::io_service::work(*m_io_service));
|
||||
}
|
||||
|
||||
/// Clears the endpoint's perpetual flag, allowing it to exit when empty
|
||||
/**
|
||||
* Clears the endpoint's perpetual flag. This will cause the endpoint's run
|
||||
* method to exit normally when it runs out of connections. If there are
|
||||
* currently active connections it will not end until they are complete.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
void stop_perpetual() {
|
||||
m_work.reset();
|
||||
}
|
||||
|
||||
/// Call back a function after a period of time.
|
||||
/**
|
||||
* Sets a timer that calls back a function after the specified period of
|
||||
@@ -691,8 +601,10 @@ public:
|
||||
lib::error_code & ec)
|
||||
{
|
||||
if (m_state != LISTENING) {
|
||||
m_elog->write(log::elevel::library,
|
||||
"asio::async_accept called from the wrong state");
|
||||
using websocketpp::error::make_error_code;
|
||||
ec = make_error_code(websocketpp::error::async_accept_not_listening);
|
||||
ec = make_error_code(websocketpp::error::invalid_state);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -757,12 +669,8 @@ protected:
|
||||
m_alog->write(log::alevel::devel, "asio::handle_accept");
|
||||
|
||||
if (boost_ec) {
|
||||
if (boost_ec == boost::system::errc::operation_canceled) {
|
||||
ret_ec = make_error_code(websocketpp::error::operation_canceled);
|
||||
} else {
|
||||
log_err(log::elevel::info,"asio handle_accept",boost_ec);
|
||||
ret_ec = make_error_code(error::pass_through);
|
||||
}
|
||||
log_err(log::elevel::devel,"asio handle_accept",boost_ec);
|
||||
ret_ec = make_error_code(error::pass_through);
|
||||
}
|
||||
|
||||
callback(ret_ec);
|
||||
@@ -1002,6 +910,10 @@ protected:
|
||||
callback(lib::error_code());
|
||||
}
|
||||
|
||||
bool is_listening() const {
|
||||
return (m_state == LISTENING);
|
||||
}
|
||||
|
||||
/// Initialize a connection
|
||||
/**
|
||||
* init is called by an endpoint once for each newly created connection.
|
||||
@@ -1054,11 +966,6 @@ private:
|
||||
bool m_external_io_service;
|
||||
acceptor_ptr m_acceptor;
|
||||
resolver_ptr m_resolver;
|
||||
work_ptr m_work;
|
||||
|
||||
// Network constants
|
||||
int m_listen_backlog;
|
||||
bool m_reuse_addr;
|
||||
|
||||
elog_type* m_elog;
|
||||
alog_type* m_alog;
|
||||
|
||||
@@ -95,10 +95,7 @@ namespace error {
|
||||
pass_through,
|
||||
|
||||
/// Required tls_init handler not present
|
||||
missing_tls_init_handler,
|
||||
|
||||
/// TLS Handshake Failed
|
||||
tls_handshake_failed,
|
||||
missing_tls_init_handler
|
||||
};
|
||||
} // namespace error
|
||||
|
||||
@@ -122,11 +119,9 @@ public:
|
||||
case error::tls_handshake_timeout:
|
||||
return "TLS handshake timed out";
|
||||
case error::pass_through:
|
||||
return "Pass through from socket policy";
|
||||
return "Pass through from underlying library";
|
||||
case error::missing_tls_init_handler:
|
||||
return "Required tls_init handler not present.";
|
||||
case error::tls_handshake_failed:
|
||||
return "TLS handshake failed";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -229,23 +229,6 @@ protected:
|
||||
lib::error_code get_ec() const {
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
/// Translate any security policy specific information about an error code
|
||||
/**
|
||||
* Translate_ec takes a boost error code and attempts to convert its value
|
||||
* to an appropriate websocketpp error code. The plain socket policy does
|
||||
* not presently provide any additional information so all errors will be
|
||||
* reported as the generic transport pass_through error.
|
||||
*
|
||||
* @since 0.4.0-beta1
|
||||
*
|
||||
* @param ec The error code to translate_ec
|
||||
* @return The translated error code
|
||||
*/
|
||||
lib::error_code translate_ec(boost::system::error_code ec) {
|
||||
// We don't know any more information about this error so pass through
|
||||
return make_error_code(transport::error::pass_through);
|
||||
}
|
||||
private:
|
||||
enum state {
|
||||
UNINITIALIZED = 0,
|
||||
|
||||
@@ -260,10 +260,11 @@ protected:
|
||||
m_hdl = hdl;
|
||||
}
|
||||
|
||||
void handle_init(init_handler callback,boost::system::error_code const & ec)
|
||||
void handle_init(init_handler callback, const
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
if (ec) {
|
||||
m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
|
||||
m_ec = socket::make_error_code(socket::error::pass_through);
|
||||
} else {
|
||||
m_ec = lib::error_code();
|
||||
}
|
||||
@@ -283,37 +284,6 @@ protected:
|
||||
void async_shutdown(socket_shutdown_handler callback) {
|
||||
m_socket->async_shutdown(callback);
|
||||
}
|
||||
|
||||
/// Translate any security policy specific information about an error code
|
||||
/**
|
||||
* Translate_ec takes a boost error code and attempts to convert its value
|
||||
* to an appropriate websocketpp error code. Any error that is determined to
|
||||
* be related to TLS but does not have a more specific websocketpp error
|
||||
* code is returned under the catch all error "tls_error".
|
||||
*
|
||||
* Non-TLS related errors are returned as the transport generic pass_through
|
||||
* error.
|
||||
*
|
||||
* @since 0.4.0-beta1
|
||||
*
|
||||
* @param ec The error code to translate_ec
|
||||
* @return The translated error code
|
||||
*/
|
||||
lib::error_code translate_ec(boost::system::error_code ec) {
|
||||
if (ec.category() == boost::asio::error::get_ssl_category()) {
|
||||
if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
|
||||
return make_error_code(transport::error::tls_short_read);
|
||||
} else {
|
||||
// We know it is a TLS related error, but otherwise don't know
|
||||
// more. Pass through as TLS generic.
|
||||
return make_error_code(transport::error::tls_error);
|
||||
}
|
||||
} else {
|
||||
// We don't know any more information about this error so pass
|
||||
// through
|
||||
return make_error_code(transport::error::pass_through);
|
||||
}
|
||||
}
|
||||
private:
|
||||
socket_type::handshake_type get_handshake_type() {
|
||||
if (m_is_server) {
|
||||
|
||||
@@ -169,13 +169,7 @@ enum value {
|
||||
tls_short_read,
|
||||
|
||||
/// Timer expired
|
||||
timeout,
|
||||
|
||||
/// read or write after shutdown
|
||||
action_after_shutdown,
|
||||
|
||||
/// Other TLS error
|
||||
tls_error,
|
||||
timeout
|
||||
};
|
||||
|
||||
class category : public lib::error_category {
|
||||
@@ -204,10 +198,6 @@ class category : public lib::error_category {
|
||||
return "TLS Short Read";
|
||||
case timeout:
|
||||
return "Timer Expired";
|
||||
case action_after_shutdown:
|
||||
return "A transport action was requested after shutdown";
|
||||
case tls_error:
|
||||
return "Generic TLS related error";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -28,9 +28,8 @@
|
||||
#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
|
||||
#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
|
||||
|
||||
#include <websocketpp/common/connection_hdl.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
#include <websocketpp/common/platforms.hpp>
|
||||
#include <websocketpp/common/connection_hdl.hpp>
|
||||
#include <websocketpp/logger/levels.hpp>
|
||||
|
||||
#include <websocketpp/transport/base/connection.hpp>
|
||||
|
||||
+12
-12
@@ -47,13 +47,13 @@ static uint16_t const uri_default_secure_port = 443;
|
||||
|
||||
class uri {
|
||||
public:
|
||||
explicit uri(std::string const & uri_string) : m_valid(false) {
|
||||
explicit uri(std::string const & uri) : m_valid(false) {
|
||||
std::string::const_iterator it;
|
||||
std::string::const_iterator temp;
|
||||
|
||||
int state = 0;
|
||||
|
||||
it = uri_string.begin();
|
||||
it = uri.begin();
|
||||
|
||||
if (std::equal(it,it+6,"wss://")) {
|
||||
m_secure = true;
|
||||
@@ -88,14 +88,14 @@ public:
|
||||
//temp = std::find(it,it2,']');
|
||||
|
||||
temp = it;
|
||||
while (temp != uri_string.end()) {
|
||||
while (temp != uri.end()) {
|
||||
if (*temp == ']') {
|
||||
break;
|
||||
}
|
||||
++temp;
|
||||
}
|
||||
|
||||
if (temp == uri_string.end()) {
|
||||
if (temp == uri.end()) {
|
||||
return;
|
||||
} else {
|
||||
// validate IPv6 literal parts
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
m_host.append(it,temp);
|
||||
}
|
||||
it = temp+1;
|
||||
if (it == uri_string.end()) {
|
||||
if (it == uri.end()) {
|
||||
state = 2;
|
||||
} else if (*it == '/') {
|
||||
state = 2;
|
||||
@@ -119,7 +119,7 @@ public:
|
||||
// IPv4 or hostname
|
||||
// extract until : or /
|
||||
while (state == 0) {
|
||||
if (it == uri_string.end()) {
|
||||
if (it == uri.end()) {
|
||||
state = 2;
|
||||
break;
|
||||
} else if (*it == '/') {
|
||||
@@ -137,7 +137,7 @@ public:
|
||||
// parse port
|
||||
std::string port = "";
|
||||
while (state == 1) {
|
||||
if (it == uri_string.end()) {
|
||||
if (it == uri.end()) {
|
||||
// state is not used after this point presently.
|
||||
// this should be re-enabled if it ever is needed in a future
|
||||
// refactoring
|
||||
@@ -159,7 +159,7 @@ public:
|
||||
}
|
||||
|
||||
m_resource = "/";
|
||||
m_resource.append(it,uri_string.end());
|
||||
m_resource.append(it,uri.end());
|
||||
|
||||
|
||||
m_valid = true;
|
||||
@@ -290,10 +290,10 @@ public:
|
||||
* @return query portion of the URI.
|
||||
*/
|
||||
std::string get_query() const {
|
||||
std::size_t found = m_resource.find('?');
|
||||
if (found != std::string::npos) {
|
||||
return m_resource.substr(found + 1);
|
||||
} else {
|
||||
std::size_t found = m_resource.find('?');
|
||||
if (found != std::string::npos) {
|
||||
return m_resource.substr(found + 1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ struct ci_less : std::binary_function<std::string, std::string, bool> {
|
||||
: public std::binary_function<unsigned char,unsigned char,bool>
|
||||
{
|
||||
bool operator() (unsigned char const & c1, unsigned char const & c2) const {
|
||||
return tolower (c1) < tolower (c2);
|
||||
return std::tolower (c1) < std::tolower (c2);
|
||||
}
|
||||
};
|
||||
bool operator() (std::string const & s1, std::string const & s2) const {
|
||||
|
||||
Reference in new issue
Block a user