Compare commits

..
128 Commits
Author SHA1 Message Date
Peter Thorson bded14b635 Merge branch 'master' into tutorials 2014-03-25 07:24:28 -05:00
Peter Thorson 002d2d0007 force the offset to be calculated first to avoid confusing MSVC's checked iterators references #342
Thank you tmoers for reporting
2014-03-25 06:15:40 -05:00
Peter Thorson d8b7cf3fdc Merge pull request #343 from zaphoyd/batched_send
Batched send
2014-03-25 06:03:23 -05:00
Peter Thorson b358e2cfe8 update changelog 2014-03-25 05:49:15 -05:00
Peter Thorson 099fdbd4f1 Merge branch 'master' into batched_send 2014-03-25 05:46:12 -05:00
Peter Thorson 19a3713b29 use libc++ for non-c++11 branches on mac os x 2014-03-24 08:07:40 -05:00
Peter Thorson cd534ad18e implements batch sending
rather than writing a single message per trip through the underlying
transport, writes are batched and sent as a group. This drastically
improves the system call / application code ratio and tcp packet
utilization rates when sending lots of small messages
2014-03-24 08:04:23 -05:00
Peter Thorson e2b7a4b9f1 fix whitespace 2014-03-16 09:59:34 -05:00
Peter Thorson e154d955ec Use __cplusplus header to enable C++11 language features 2014-03-16 09:26:51 -05:00
Peter Thorson 2207348abb Merge pull request #340 from dschmidt/master
Use _WIN32 instead of WIN32 preprocessor directive
2014-03-16 09:09:14 -05:00
Dominik Schmidt 77495d4076 Use _WIN32 instead of WIN32 preprocessor directive and fix warnings 2014-03-14 17:42:05 +01:00
Peter Thorson 8752687bc8 Merge pull request #339 from jefferai/master
Add define to allow disabling threading entirely
2014-03-12 13:15:24 -05:00
Jeff Mitchell 0f3a36a630 Add define to allow disabling threading entirely, for when using an
iostream-based client.
2014-03-12 10:46:33 -04:00
Peter Thorson edb26d7721 listen errors are now reported to the caller 2014-03-06 19:01:11 -06:00
Peter Thorson b2d698d3ca include hdl in message actions
This is not strictly necessary for the broadcast server example. As
this example is commonly used as a starting point for new programs the
lack of hdl on message action is a source of surprising behavior.
2014-03-06 11:59:38 -06:00
Peter Thorson 9d8eced9a1 Refactored server_endpoint to allow end users to access start_accept error states references #335
also more documentation
2014-03-05 08:06:14 -06:00
Peter Thorson fd0c62a8e4 add TLS config and types to asio integration tests 2014-03-05 07:27:41 -06:00
Peter Thorson 1a68f4b907 Merge pull request #335 from aydany/master
Require a valid con before calling terminate
2014-03-05 07:23:19 -06:00
Aydan Yumerefendi c3dfb25181 Merge remote-tracking branch 'upstream/master'
Conflicts:
	websocketpp/roles/server_endpoint.hpp
2014-03-04 21:19:24 -05:00
Aydan Yumerefendi 5a5d8321bd Require a valid con before calling terminate
If get_connection returns null, e.g., if during stop the tls_init
handler is removed from the server, con->terminate will cause a crash.
2014-03-04 21:02:42 -05:00
Peter Thorson b805689f9c whitespace and don't accept if we know the transport isn't listening references #334 2014-03-04 06:45:00 -06:00
Peter Thorson 7eedf25500 Merge pull request #334 from aydany/master
Avoid leak when stopping server
2014-03-04 06:18:34 -06:00
Aydan Yumerefendi 3ca53d0064 Avoid leak when stopping server
If accept fails, e.g., because the server is no longer listening the
connection object is not terminated and it results in a memory leak:
connection -> handler -> connection.
2014-03-03 21:51:56 -05:00
Peter Thorson 5e2fadd826 pass better information about TLS handshake failures 2014-03-03 17:09:55 -06:00
Peter Thorson 840bb6b59e code style and whitespace 2014-03-03 10:32:04 -06:00
Peter Thorson fe00a84f7b Merge pull request #333 from aydany/master
Fix memory leak when init_asio fails
2014-03-03 10:24:11 -06:00
Aydan Yumerefendi 337bf2fb0a Fix memory leak on create
If the socket fails to initialize, e.g., by omitting the tis init
handler, the connection is leaked, since there is a circular reference
between the connection and its async_read/async_write handlers. To fix
this, check the error code, and reset the handler is an error has
occurred.
2014-03-03 11:08:24 -05:00
Peter Thorson c5b5492e5c fix memory leak when a connection fails references #323 2014-03-02 22:12:38 -06:00
Peter Thorson af15537205 add debug header to integration tests 2014-03-02 22:12:14 -06:00
Peter Thorson bdeb4ad680 code style and documentation 2014-03-02 21:39:34 -06:00
Peter Thorson faad2ec854 add copyright info to debug client 2014-03-02 19:43:25 -06:00
Peter Thorson dc06049df9 add debug server example 2014-03-02 19:43:11 -06:00
Peter Thorson 3e53c74c3f Merge pull request #332 from zaphoyd/tutorials
Update examples and tutorials
2014-03-02 19:27:56 -06:00
Peter Thorson a5a30e554e rename app_client to utility_client and set utility_client to be the final step of the utility_client tutorial 2014-03-02 19:21:17 -06:00
Peter Thorson 9c8762c961 rename old utility client to debug client 2014-03-02 19:20:42 -06:00
Peter Thorson 797ce35c5a more renaming and adding license notes 2014-03-02 19:14:59 -06:00
Peter Thorson 30da3a967b renaming and re-titling the app client tutorial to utility client 2014-03-02 19:10:24 -06:00
Peter Thorson e1547bbde8 stray characters 2014-03-02 19:04:13 -06:00
Peter Thorson 37f69cc19b fix typo 2014-03-02 19:03:04 -06:00
Peter Thorson 93d9b5f820 Formatting fixes for app client tutorial step 5 2014-03-02 19:02:11 -06:00
Peter Thorson 98fd183210 Finish app client tutorial step 5 (closing) 2014-03-02 18:53:55 -06:00
Peter Thorson 093806f860 Merge branch 'master' into tutorials 2014-03-02 12:21:32 -06:00
Peter Thorson 1da45144e9 whitespace 2014-03-02 12:21:04 -06:00
Peter Thorson 5f56976290 Add method to look up a human readable string given a close code value 2014-03-02 12:19:50 -06:00
Peter Thorson 40ed74a141 Use clang on OS X by default for building examples and tests 2014-03-02 12:17:03 -06:00
Peter Thorson fbc9db2b5d work on close step 2014-03-02 12:06:37 -06:00
Peter Thorson f87c9d4360 need the line breaks to work here 2014-03-02 08:33:41 -06:00
Peter Thorson 99461184d3 Merge pull request #331 from zaphoyd/tls_short_read
Tls short read related changes references #263
2014-02-28 09:04:59 -06:00
Peter Thorson 9eff0db756 update changelog 2014-02-28 09:05:53 -06:00
Peter Thorson 86325b1838 cleans up the handling of TLS related transport errors
Specifically: TLS short read is separated from other TLS errors. Other
TLS errors are also not separated from generic “pass through” errors.

TLS short read is now considered “expected” during socket shutdown and
is not reported as an error.
2014-02-28 09:02:26 -06:00
Peter Thorson 2d2312a1d7 add socket/security policies to translate asio -> websocketpp error codes
The reason these were added to the socket policies rather than higher
up is that in some cases translating error codes requires information
from headers and libraries that are not present except when certain
socket policies are being used. For example, SSL/TLS related errors can
only be translated using openssl, which is only pulled in with the TLS
socket policy.
2014-02-28 09:00:45 -06:00
Peter Thorson afc28be004 whitespace 2014-02-28 08:27:29 -06:00
Peter Thorson b7c5a2e791 tls short read in state closed is no longer an error 2014-02-28 08:27:14 -06:00
Peter Thorson d36925ce7d Add new generic TLS error code 2014-02-28 08:26:50 -06:00
Peter Thorson 92dfe9ef97 clarify commands section and new members/methods of websocket_endpoint 2014-02-23 15:46:51 -06:00
Peter Thorson 2ab02197fd Fleshes out step 4 (opening connections) 2014-02-23 15:31:54 -06:00
Peter Thorson 89ac45e60a additional work on app client tutorial 2014-02-23 09:34:34 -06:00
Peter Thorson 713a05679c Merge pull request #325 from zaphoyd/max-message-size
error handling changes that ended up on wrong branch
2014-02-18 07:59:20 -06:00
Peter Thorson 0fecf023ef refactor connection error handling code 2014-02-18 08:01:16 -06:00
Peter Thorson ffdf5f842b reclassify action after shutdown as info rather than fatal references #62 2014-02-18 07:59:43 -06:00
Peter Thorson d425caea80 Fix misclassified error log entry 2014-02-18 07:58:49 -06:00
Peter Thorson ce2c1d6e34 whitespace 2014-02-13 11:18:44 -06:00
Peter Thorson 7ceeaa4f9c fix documentation spelling error 2014-02-13 11:10:47 -06:00
Peter Thorson 883410bb77 Fix an issue where custom timeout values weren't being propagated from endpoints to new connections 2014-02-12 15:07:09 -06:00
Peter Thorson 724974c758 Merge pull request #322 from zaphoyd/max-message-size
Adds the ability to specify a max message size
2014-02-10 20:55:02 -06:00
Peter Thorson 1622cdde04 Retry the travis build 2014-02-10 20:51:37 -06:00
Peter Thorson 180543b9e6 Adds the ability to specify a maximum message size 2014-02-10 09:21:07 -06:00
Peter Thorson 7c08cae15b fix bug that caused all error close frames to have a blank code 2014-02-10 09:17:43 -06:00
Peter Thorson 81e0c44b34 update debug config with max_message_size constant 2014-02-10 09:16:47 -06:00
Peter Thorson 722a67a910 add message payload size reservation 2014-02-10 08:39:20 -06:00
Peter Thorson ba6320ba1c Add maximum message size functionality to processors 2014-02-10 08:38:54 -06:00
Peter Thorson d9aa498310 whitespace cleanup 2014-02-10 08:37:47 -06:00
Peter Thorson 47c46a2fbc Fleshes out the remainder of app tutorial step 3 2014-02-07 08:10:10 -06:00
Peter Thorson 9401cf00ce rework app tutorial step 2 2014-02-07 07:26:00 -06:00
Peter Thorson acedd4db49 tutorial formatting 2014-02-07 06:59:28 -06:00
Peter Thorson 91617150e1 more tutorial formatting tests 2014-02-07 06:56:48 -06:00
Peter Thorson c7463afb06 tutorial updates 2014-02-07 06:49:45 -06:00
Peter Thorson ba90910f6e Fix an issue where set_open_handshake_timeout was ignored for servers 2014-02-05 21:00:30 -06:00
Peter Thorson bee2c03aa0 code style updates 2014-02-05 20:56:54 -06:00
Peter Thorson 8e86ddcf5a fix typos in comments 2014-02-02 19:28:23 -06:00
Peter Thorson fe85de763e Adds configurable SO_REUSEADDR option. references #311 2014-02-02 18:12:30 -06:00
Peter Thorson ca97dd1fb8 Switch to c-based tolower rather than c++ to avoid confusing Visual Studio references #287 2014-02-02 17:04:50 -06:00
Peter Thorson c7ec97795a add C++11 nullptr support detection 2014-02-02 17:03:41 -06:00
Peter Thorson 6ee7c368bb remove unused variables 2014-02-02 15:14:12 -06:00
Peter Thorson 2000746be7 correct misc minor warnings 2014-02-02 15:12:42 -06:00
Peter Thorson c84cc110aa Corrects more obscure shadowed variable cases 2 2014-02-02 13:17:17 -06:00
Peter Thorson 09940a5c0b Corrects more obscure shadowed variables cases 2014-02-02 13:12:52 -06:00
Peter Thorson bb21e3cf95 Fix use of variable names that shadow function parameters. references #318 2014-02-02 11:10:39 -06:00
Peter Thorson 5f3b92c9e4 Fix compile time conflict with Visual Studio's MIN/MAX macros. 2014-02-02 10:58:02 -06:00
Peter Thorson 14074666a7 disable some debugging output 2014-02-02 10:55:31 -06:00
Peter Thorson bb431ee0d2 Fix a crash following use of the stop_listening function. 2014-01-26 20:53:17 -06:00
Peter Thorson 373f180638 re-classify some errors with more specific codes and more appropriate logging levels 2014-01-26 20:50:31 -06:00
Peter Thorson 021ac0599c add additional error codes 2014-01-26 20:49:20 -06:00
Peter Thorson e44463583b Fix a crash when parsing empty HTTP headers 2014-01-26 19:39:55 -06:00
Peter Thorson 4393a2562b Add the ability to pause reading on a connection 2014-01-25 18:46:44 -06:00
Peter Thorson 780f7683a4 add typedefs for iostream clients and servers to integration test suite 2014-01-25 18:29:54 -06:00
Peter Thorson 8f359e6d35 typo 2013-12-14 09:37:16 -06:00
Peter Thorson ebb65c81e2 Allow disabling open, close, and pong timers entirely
Set their duration to zero to do this
2013-12-14 09:31:58 -06:00
Peter Thorson c7fda5a960 remove debug output from test 2013-12-14 09:31:58 -06:00
Peter Thorson 6d4514246f separate step 2 and 3 2013-12-14 07:41:21 -06:00
Peter Thorson c2f2cb951b Fixes some calls to empty lib::functions, references #310 2013-12-10 22:02:22 -06:00
Peter Thorson 827f3678bb Fixes a leak of connection objects when using asio transport references #310 2013-11-26 22:05:26 -06:00
Peter Thorson 57cd0e286e roadmap / wishlist for broadcast tutorial 2013-11-25 08:20:52 -06:00
Peter Thorson 87cb5036a4 initial work on app client tutorial 2013-11-25 08:16:14 -06:00
Peter Thorson f05f18b384 clean up chat tutorial a bit 2013-11-25 08:15:20 -06:00
Peter Thorson 01963a23ea working copy of app client tutorial example 2013-11-21 20:17:39 -06:00
Peter Thorson 18036d2b01 sets mutex to mutable to allow use in const accessors references #292 2013-11-05 07:52:31 -06:00
Peter Thorson 17bd89bd80 code style 2013-11-05 07:50:20 -06:00
Peter Thorson f59f301b91 fix typo in testee server 2013-11-04 17:36:55 -06:00
Peter Thorson 319507f4af update testee_server to disable nagle algorithm 2013-11-04 16:50:45 -06:00
Peter Thorson 06cdf43dcf Add start_perpetual and stop_perpetual to asio transport
These may be used to replace manually managed `asio::io_service::work`
objects
2013-11-04 16:50:35 -06:00
Peter Thorson f8f945433f code style 2013-11-04 07:40:11 -06:00
Peter Thorson a4f96cf0c3 groundwork for unclean close options 2013-11-04 07:40:06 -06:00
Peter Thorson 7032e6940f adds tcp pre and post init setting functionality to endpoint 2013-11-03 13:32:17 -06:00
Peter Thorson bfd50e1ee1 locks and spacing 2013-11-03 13:25:21 -06:00
Peter Thorson 415533f7e7 labels changes feature vs bug 2013-11-03 12:44:28 -06:00
Peter Thorson b87bfc068e Allow setting pong and handshake timeouts at runtime 2013-11-03 12:40:55 -06:00
Peter Thorson dbb5f963c8 update testee server to use extended listen backlog 2013-11-03 12:31:54 -06:00
Peter Thorson a1872c3790 Merge branch 'master' of https://github.com/zaphoyd/websocketpp
Conflicts:
	changelog.md
2013-11-02 19:34:01 -05:00
Peter Thorson 8993b34b3d Allows changing the listen backlog queue length 2013-11-02 19:32:38 -05:00
Peter Thorson 7ae83b9efd update changelog 2013-11-02 18:25:55 -05:00
Peter Thorson 430c49d394 splits tcp init into pre and post init 2013-11-02 18:25:21 -05:00
Peter Thorson 3e5404e1b1 split handler allocator into separate one for reads and writes 2013-11-02 18:24:14 -05:00
Peter Thorson 8524a1a272 add program arguments 2013-11-02 18:22:58 -05:00
Peter Thorson 6f79894ea5 add variable port and thread count to testee server 2013-11-02 17:59:55 -05:00
Peter Thorson 5e803b57f7 add uri unit test 2013-11-01 16:05:46 -05:00
Peter Thorson 6a3e886f0b update HTTP parser tests to use more specific test checks 2013-10-25 07:23:30 -05:00
Peter Thorson ac9351f9b3 strips LWS from incoming headers per RFC2616. fixes #301 2013-10-25 07:23:06 -05:00
72 changed files with 3890 additions and 1465 deletions

No files matched your search

+11
View File
@@ -79,6 +79,8 @@ 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:
@@ -98,11 +100,13 @@ 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
@@ -142,6 +146,7 @@ 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
@@ -222,6 +227,12 @@ 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)
+60 -8
View File
@@ -1,12 +1,64 @@
HEAD
- 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)
- 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
0.3.0-alpha4 - 2013-10-11
- HTTP requests ending normally are no longer logged as errors. Thank you Banaan
@@ -34,7 +34,8 @@ enum action_type {
struct action {
action(action_type t, connection_hdl h) : type(t), hdl(h) {}
action(action_type t, server::message_ptr m) : type(t), msg(m) {}
action(action_type t, connection_hdl h, server::message_ptr m)
: type(t), hdl(h), msg(m) {}
action_type type;
websocketpp::connection_hdl hdl;
@@ -92,7 +93,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,msg));
m_actions.push(action(MESSAGE,hdl,msg));
lock.unlock();
m_action_cond.notify_one();
}
@@ -111,13 +112,13 @@ public:
lock.unlock();
if (a.type == SUBSCRIBE) {
unique_lock<mutex> lock(m_connection_lock);
unique_lock<mutex> con_lock(m_connection_lock);
m_connections.insert(a.hdl);
} else if (a.type == UNSUBSCRIBE) {
unique_lock<mutex> lock(m_connection_lock);
unique_lock<mutex> con_lock(m_connection_lock);
m_connections.erase(a.hdl);
} else if (a.type == MESSAGE) {
unique_lock<mutex> lock(m_connection_lock);
unique_lock<mutex> con_lock(m_connection_lock);
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
@@ -142,13 +143,13 @@ private:
int main() {
try {
broadcast_server server;
broadcast_server server_instance;
// Start a thread to run the processing loop
thread t(bind(&broadcast_server::process_messages,&server));
thread t(bind(&broadcast_server::process_messages,&server_instance));
// Run the asio loop with the main thread
server.run(9002);
server_instance.run(9002);
t.join();
+24
View File
@@ -0,0 +1,24 @@
## 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')
+152
View File
@@ -0,0 +1,152 @@
/*
* 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;
}
}
+10
View File
@@ -0,0 +1,10 @@
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 ()
+23
View File
@@ -0,0 +1,23 @@
## 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')
+92
View File
@@ -0,0 +1,92 @@
/*
* 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;
}
}
+2 -3
View File
@@ -6,7 +6,6 @@ Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
Import('tls_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
@@ -15,10 +14,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] + [tls_libs]
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('testee_server', ["testee_server.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('testee_server', ["testee_server.cpp"], LIBS = ALL_LIBS)
Return('prgs')
+17 -43
View File
@@ -25,37 +25,10 @@
*
*/
//#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/config/asio.hpp>
#include <websocketpp/config/asio_no_tls.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;
@@ -67,17 +40,12 @@ 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;
typedef core::endpoint_base endpoint_base;
static bool const enable_multithreading = true;
static bool const enable_multithreading = false;
struct transport_config : public core::transport_config {
typedef core::concurrency_type concurrency_type;
@@ -86,7 +54,7 @@ struct testee_config : public websocketpp::config::asio {
typedef core::request_type request_type;
typedef core::response_type response_type;
static bool const enable_multithreading = true;
static bool const enable_multithreading = false;
};
typedef websocketpp::transport::asio::endpoint<transport_config>
@@ -112,8 +80,13 @@ 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;
@@ -124,26 +97,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();
// Register our message handler
testee_server.set_message_handler(bind(&on_message,&testee_server,::_1,::_2));
//testee_server.set_tls_init_handler(bind(&on_tls_init,::_1));
testee_server.set_socket_init_handler(bind(&on_socket_init,::_1,::_2));
// Listen on port
// Listen on specified port with extended listen backlog
testee_server.set_listen_backlog(8192);
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 {
@@ -157,6 +130,7 @@ 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) {
+11
View File
@@ -0,0 +1,11 @@
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 ()
+2 -3
View File
@@ -6,7 +6,6 @@ Import('env_cpp11')
Import('boostlibs')
Import('platform_libs')
Import('polyfill_libs')
Import('tls_libs')
env = env.Clone ()
env_cpp11 = env_cpp11.Clone ()
@@ -15,10 +14,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] + [tls_libs]
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('utility_client', ["utility_client.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('utility_client', ["utility_client.cpp"], LIBS = ALL_LIBS)
Return('prgs')
+230 -126
View File
@@ -1,166 +1,270 @@
/**
* This example is presently used as a scratch space. It may or may not be broken
* at any given time.
/*
* 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.
*/
#include <websocketpp/config/asio_client.hpp>
// **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 <chrono>
#include <map>
#include <string>
#include <sstream>
typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
typedef websocketpp::client<websocketpp::config::asio_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 {
class connection_metadata {
public:
typedef perftest type;
typedef std::chrono::duration<int,std::micro> dur_type;
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
perftest () {
m_endpoint.set_access_channels(websocketpp::log::alevel::none);
m_endpoint.set_error_channels(websocketpp::log::elevel::none);
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")
{}
// Initialize ASIO
m_endpoint.init_asio();
void on_open(client * c, websocketpp::connection_hdl hdl) {
m_status = "Open";
// 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));
client::connection_ptr con = c->get_con_from_hdl(hdl);
m_server = con->get_response_header("Server");
}
void start(std::string uri) {
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) {
m_endpoint.get_alog().write(websocketpp::log::alevel::app,ec.message());
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
return -1;
}
//con->set_proxy("http://humupdates.uchicago.edu:8443");
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);
// Start the ASIO io_service run loop
m_start = std::chrono::high_resolution_clock::now();
m_endpoint.run();
return new_id;
}
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();
}
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;
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;
}
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,"");
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 {
m_msg_count++;
m_endpoint.send(hdl, m_msg);
return metadata_it->second;
}
}
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;
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;
con_list m_connection_list;
int m_next_id;
};
int main(int argc, char* argv[]) {
std::string uri = "wss://echo.websocket.org";
int main() {
bool done = false;
std::string input;
websocket_endpoint endpoint;
if (argc == 2) {
uri = argv[1];
}
while (!done) {
std::cout << "Enter Command: ";
std::getline(std::cin, input);
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;
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;
}
+1 -1
View File
@@ -17,7 +17,7 @@ Complete & Tested:
- close_handler
- echo_server & echo_server_tls
Implimented, needs more testing
Implemented, needs more testing
- TLS support
- External io_service support
- socket_init_handler
+28 -2
View File
@@ -80,8 +80,7 @@ struct stub_config : public websocketpp::config::core {
};
struct connection_setup {
connection_setup(bool server)
: c(server,"",alog,elog,rng) {}
connection_setup(bool p_is_server) : c(p_is_server, "", alog, elog, rng) {}
websocketpp::lib::error_code ec;
stub_config::alog_type alog;
@@ -193,6 +192,33 @@ 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 ) {
+103 -56
View File
@@ -355,6 +355,26 @@ 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;
@@ -655,12 +675,12 @@ BOOST_AUTO_TEST_CASE( old_http_version ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 41 );
BOOST_CHECK_EQUAL( pos, 41 );
BOOST_CHECK( r.ready() == true );
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_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_AUTO_TEST_CASE( new_http_version1 ) {
@@ -678,12 +698,12 @@ BOOST_AUTO_TEST_CASE( new_http_version1 ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 42 );
BOOST_CHECK_EQUAL( pos, 42 );
BOOST_CHECK( r.ready() == true );
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_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_AUTO_TEST_CASE( new_http_version2 ) {
@@ -701,12 +721,12 @@ BOOST_AUTO_TEST_CASE( new_http_version2 ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 43 );
BOOST_CHECK_EQUAL( pos, 43 );
BOOST_CHECK( r.ready() == true );
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" );
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" );
}
/* commented out due to not being implemented yet
@@ -726,7 +746,7 @@ BOOST_AUTO_TEST_CASE( new_http_version3 ) {
}
BOOST_CHECK( exception == true );
}
}*/
BOOST_AUTO_TEST_CASE( header_whitespace1 ) {
websocketpp::http::parser::request r;
@@ -743,12 +763,12 @@ BOOST_AUTO_TEST_CASE( header_whitespace1 ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 43 );
BOOST_CHECK_EQUAL( pos, 43 );
BOOST_CHECK( r.ready() == true );
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_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_AUTO_TEST_CASE( header_whitespace2 ) {
@@ -766,13 +786,13 @@ BOOST_AUTO_TEST_CASE( header_whitespace2 ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 40 );
BOOST_CHECK_EQUAL( pos, 40 );
BOOST_CHECK( r.ready() == true );
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_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_AUTO_TEST_CASE( header_aggregation ) {
websocketpp::http::parser::request r;
@@ -789,12 +809,12 @@ BOOST_AUTO_TEST_CASE( header_aggregation ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 61 );
BOOST_CHECK_EQUAL( pos, 61 );
BOOST_CHECK( r.ready() == true );
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_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_AUTO_TEST_CASE( wikipedia_example_response ) {
@@ -813,15 +833,42 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 159 );
BOOST_CHECK_EQUAL( pos, 159 );
BOOST_CHECK( r.headers_ready() == true );
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_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_AUTO_TEST_CASE( plain_http_response ) {
@@ -840,21 +887,21 @@ BOOST_AUTO_TEST_CASE( plain_http_response ) {
}
BOOST_CHECK( exception == false );
BOOST_CHECK( pos == 405 );
BOOST_CHECK_EQUAL( pos, 405 );
BOOST_CHECK( r.headers_ready() == true );
BOOST_CHECK( r.ready() == true );
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_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_AUTO_TEST_CASE( parse_istream ) {
@@ -889,7 +936,7 @@ BOOST_AUTO_TEST_CASE( write_request_basic ) {
r.set_method("GET");
r.set_uri("/");
BOOST_CHECK( r.raw() == raw );
BOOST_CHECK_EQUAL( r.raw(), raw );
}
BOOST_AUTO_TEST_CASE( write_request_with_header ) {
@@ -902,7 +949,7 @@ BOOST_AUTO_TEST_CASE( write_request_with_header ) {
r.set_uri("/");
r.replace_header("Host","http://example.com");
BOOST_CHECK( r.raw() == raw );
BOOST_CHECK_EQUAL( r.raw(), raw );
}
BOOST_AUTO_TEST_CASE( write_request_with_body ) {
@@ -917,5 +964,5 @@ BOOST_AUTO_TEST_CASE( write_request_with_body ) {
r.replace_header("Content-Type","application/x-www-form-urlencoded");
r.set_body("licenseID=string&content=string&paramsXML=string");
BOOST_CHECK( r.raw() == raw );
BOOST_CHECK_EQUAL( r.raw(), raw );
}
+2
View File
@@ -45,6 +45,8 @@ 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;
static const size_t max_message_size = 16000000;
};
struct processor_setup {
+2
View File
@@ -50,6 +50,8 @@ struct stub_config {
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;
+2
View File
@@ -50,6 +50,8 @@ struct stub_config {
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;
+32
View File
@@ -60,6 +60,7 @@ 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;
};
@@ -81,6 +82,7 @@ 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;
};
@@ -489,6 +491,36 @@ 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);
+1 -1
View File
@@ -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);
}
}
+179 -5
View File
@@ -30,8 +30,11 @@
#include <websocketpp/common/thread.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/config/asio_no_tls_client.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/server.hpp>
#include <websocketpp/client.hpp>
@@ -77,9 +80,57 @@ 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;
@@ -103,6 +154,7 @@ void run_server(server * s, int port, bool log = false) {
}
s->init_asio();
s->set_reuse_addr(true);
s->listen(port);
s->start_accept();
@@ -117,9 +169,11 @@ 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);
}
c.init_asio();
websocketpp::lib::error_code ec;
c.init_asio(ec);
c.set_reuse_addr(true);
BOOST_CHECK(!ec);
client::connection_ptr con = c.get_connection(uri,ec);
BOOST_CHECK( !ec );
c.connect(con);
@@ -127,6 +181,14 @@ 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)
{
@@ -394,7 +456,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();
}
@@ -430,3 +492,115 @@ 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;
}
+19 -19
View File
@@ -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(const websocketpp::lib::error_code& e) {
void handle_op(websocketpp::lib::error_code const & e) {
ec = e;
}
@@ -115,18 +115,18 @@ struct stub_con : public iostream_con {
};
// Stubs
config::alog_type a;
config::elog_type e;
config::alog_type alogger;
config::elog_type elogger;
BOOST_AUTO_TEST_CASE( const_methods ) {
iostream_con::ptr con(new iostream_con(true,a,e));
iostream_con::ptr con(new iostream_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
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,a,e));
stub_con::ptr con(new stub_con(true,alogger,elogger));
BOOST_CHECK_EQUAL(con.use_count(), 1);
+7
View File
@@ -229,6 +229,13 @@ 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/");
@@ -0,0 +1,17 @@
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
@@ -1,45 +0,0 @@
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
============
[![Build Status](https://travis-ci.org/zaphoyd/websocketpp.png)](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
-62
View File
@@ -11,65 +11,3 @@ 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
============
[![Build Status](https://travis-ci.org/zaphoyd/websocketpp.png)](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
+56
View File
@@ -0,0 +1,56 @@
/*
* 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;
}
+61
View File
@@ -0,0 +1,61 @@
/*
* 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;
}
+81
View File
@@ -0,0 +1,81 @@
/*
* 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;
}
+202
View File
@@ -0,0 +1,202 @@
/*
* 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
*/
+280
View File
@@ -0,0 +1,280 @@
/*
* 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
*/
+719
View File
@@ -0,0 +1,719 @@
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:
+60
View File
@@ -51,6 +51,26 @@ 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;
@@ -179,6 +199,46 @@ 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
+1 -1
View File
@@ -34,7 +34,7 @@ namespace websocketpp {
/// A handle to uniquely identify a connection.
/**
* This type uniquely identifies a connection. It is implimented as a weak
* This type uniquely identifies a connection. It is implemented as a weak
* pointer to the connection in question. This provides uniqueness across
* multiple endpoints and ensures that IDs never conflict or run out.
*
+27 -3
View File
@@ -41,9 +41,11 @@
#endif
#ifdef _WEBSOCKETPP_CPP11_STL_
// This flag indicates that all of the C++11 language features are available
// to us.
#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
#ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
#endif
@@ -53,6 +55,9 @@
#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_
@@ -90,6 +95,25 @@
#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
View File
@@ -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'};
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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
+1 -1
View File
@@ -32,7 +32,7 @@
#define __STDC_LIMIT_MACROS 1
#endif
#if WIN32 && (_MSC_VER < 1600)
#if defined (_WIN32) && defined (_MSC_VER) && (_MSC_VER < 1600)
#include <boost/cstdint.hpp>
using boost::int8_t;
+12
View File
@@ -215,6 +215,18 @@ 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;
+20
View File
@@ -34,7 +34,11 @@
#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>
@@ -68,7 +72,11 @@ 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;
@@ -216,6 +224,18 @@ 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;
+12
View File
@@ -216,6 +216,18 @@ 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;
+176 -11
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -42,6 +42,7 @@
#include <algorithm>
#include <iostream>
#include <queue>
#include <sstream>
#include <string>
#include <vector>
@@ -279,9 +280,9 @@ private:
};
public:
explicit connection(bool is_server, std::string const & ua, alog_type& alog,
explicit connection(bool p_is_server, std::string const & ua, alog_type& alog,
elog_type& elog, rng_type & rng)
: transport_con_type(is_server,alog,elog)
: transport_con_type(p_is_server, alog, elog)
, m_handle_read_frame(lib::bind(
&type::handle_read_frame,
this,
@@ -294,12 +295,17 @@ 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_is_server(is_server)
, m_read_flag(true)
, m_is_server(p_is_server)
, m_alog(alog)
, m_elog(elog)
, m_rng(rng)
@@ -452,6 +458,111 @@ 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 //
//////////////////////////////////
@@ -539,9 +650,46 @@ 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
/**
@@ -998,7 +1146,7 @@ public:
message_ptr get_message(websocketpp::frame::opcode::value op, size_t size)
const
{
return m_msg_manager->get_outgoing_message(op, size);
return m_msg_manager->get_message(op, size);
}
void start();
@@ -1016,8 +1164,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 handle_read_frame(lib::error_code const & ec, size_t bytes_transferred);
void read_frame();
/// Get array of WebSocket protocol versions that this connection supports.
const std::vector<int>& get_supported_versions() const;
@@ -1205,12 +1353,20 @@ 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
const std::string m_user_agent;
std::string const m_user_agent;
/// Pointer to the connection handle
connection_hdl m_connection_hdl;
@@ -1227,6 +1383,12 @@ 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
@@ -1289,9 +1451,9 @@ private:
*/
std::vector<transport::buffer> m_send_buffer;
/// a pointer to hold on to the current message being written to keep it
/// a list of pointers to hold on to the messages being written to keep them
/// from going out of scope before the write is complete.
message_ptr m_current_msg;
std::vector<message_ptr> m_current_msgs;
/// True if there is currently an outstanding transport write
/**
@@ -1299,6 +1461,9 @@ 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;
+157 -5
View File
@@ -87,18 +87,22 @@ public:
typedef lib::shared_ptr<connection_weak_ptr> hdl_type;
explicit endpoint(bool is_server)
explicit endpoint(bool p_is_server)
: m_alog(config::alog_level, &std::cout)
, m_elog(config::elog_level, &std::cerr)
, m_user_agent(::websocketpp::user_agent)
, m_is_server(is_server)
, m_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_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
@@ -269,6 +273,112 @@ 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 */
/*************************************/
@@ -284,6 +394,43 @@ 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
@@ -415,13 +562,18 @@ 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
mutex_type m_mutex;
mutable mutex_type m_mutex;
};
} // namespace websocketpp
+15 -4
View File
@@ -114,7 +114,14 @@ enum value {
close_handshake_timeout,
/// Invalid port in URI
invalid_port
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
}; // enum value
@@ -176,6 +183,10 @@ 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";
}
@@ -205,9 +216,9 @@ namespace websocketpp {
class exception : public std::exception {
public:
exception(std::string const & msg,
error::value code = error::general)
: m_msg(msg),m_code(code) {}
exception(std::string const & msg, error::value p_code = error::general)
: m_msg(msg), m_code(p_code) {}
~exception() throw() {}
virtual char const * what() const throw() {
@@ -28,8 +28,10 @@
#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>
@@ -48,8 +50,8 @@ namespace extensions {
/**
* ### permessage-deflate interface
*
* **is_implimented**\n
* `bool is_implimented()`\n
* **is_implemented**\n
* `bool is_implemented()`\n
* Returns whether or not the object impliments the extension or not
*
* **is_enabled**\n
@@ -433,7 +435,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
@@ -487,7 +489,6 @@ 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()));
@@ -497,7 +498,7 @@ public:
m_dstate.avail_out = m_compress_buffer_size;
m_dstate.next_out = m_compress_buffer.get();
ret = deflate(&m_dstate, Z_SYNC_FLUSH);
deflate(&m_dstate, Z_SYNC_FLUSH);
output = m_compress_buffer_size - m_dstate.avail_out;
+2 -2
View File
@@ -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 Implimented";
return "Not Implemented";
case bad_gateway:
return "Bad Gateway";
case service_unavailable:
+2 -2
View File
@@ -147,8 +147,8 @@ inline void parser::process_header(std::string::iterator begin,
throw exception("Invalid header line",status_code::bad_request);
}
append_header(std::string(begin,cursor),
std::string(cursor+sizeof(header_separator)-1,end));
append_header(strip_lws(std::string(begin,cursor)),
strip_lws(std::string(cursor+sizeof(header_separator)-1,end)));
}
inline std::string parser::raw_headers() const {
+10 -10
View File
@@ -38,15 +38,15 @@ namespace http {
namespace parser {
inline bool request::parse_complete(std::istream& s) {
std::string request;
std::string req;
// get status line
std::getline(s, request);
std::getline(s, req);
if (request[request.size()-1] == '\r') {
request.erase(request.end()-1);
if (req[req.size()-1] == '\r') {
req.erase(req.end()-1);
std::stringstream ss(request);
std::stringstream ss(req);
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 raw;
std::stringstream ret;
raw << m_method << " " << m_uri << " " << get_version() << "\r\n";
raw << raw_headers() << "\r\n" << m_body;
ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
ret << raw_headers() << "\r\n" << m_body;
return raw.str();
return ret.str();
}
inline void request::set_method(const std::string& method) {
+10 -10
View File
@@ -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 response;
std::string line;
// get status line
std::getline(s, response);
std::getline(s, line);
if (response[response.size()-1] == '\r') {
response.erase(response.end()-1);
if (line[line.size()-1] == '\r') {
line.erase(line.end()-1);
std::stringstream ss(response);
std::stringstream ss(line);
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 raw;
std::stringstream ret;
raw << get_version() << " " << m_status_code << " " << m_status_msg;
raw << "\r\n" << raw_headers() << "\r\n";
ret << get_version() << " " << m_status_code << " " << m_status_msg;
ret << "\r\n" << raw_headers() << "\r\n";
raw << m_body;
ret << m_body;
return raw.str();
return ret.str();
}
inline void response::set_status(status_code::value code) {
+10
View File
@@ -367,6 +367,16 @@ 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
+285 -231
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,6 +28,7 @@
#ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP
#define WEBSOCKETPP_CONNECTION_IMPL_HPP
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/processors/processor.hpp>
@@ -75,17 +76,17 @@ template <typename config>
lib::error_code connection<config>::send(const std::string& payload,
frame::opcode::value op)
{
message_ptr msg = m_msg_manager->get_outgoing_message(op,payload.size());
message_ptr msg = m_msg_manager->get_message(op,payload.size());
msg->append_payload(payload);
return send(msg);
}
template <typename config>
lib::error_code connection<config>::send(void const * payload, size_t len,
lib::error_code connection<config>::send(const void* payload, size_t len,
frame::opcode::value op)
{
message_ptr msg = m_msg_manager->get_outgoing_message(op,len);
message_ptr msg = m_msg_manager->get_message(op,len);
msg->append_payload(payload,len);
return send(msg);
@@ -111,7 +112,7 @@ lib::error_code connection<config>::send(typename config::message_type::ptr msg)
write_push(outgoing_msg);
needs_writing = !m_write_flag && !m_send_queue.empty();
} else {
outgoing_msg = m_msg_manager->get_outgoing_message(msg->get_opcode(),0);
outgoing_msg = m_msg_manager->get_message();
if (!outgoing_msg) {
return error::make_error_code(error::no_outgoing_buffers);
@@ -139,8 +140,7 @@ lib::error_code connection<config>::send(typename config::message_type::ptr msg)
}
template <typename config>
void connection<config>::ping(std::string const & payload, lib::error_code & ec)
{
void connection<config>::ping(const std::string& payload, lib::error_code& ec) {
m_alog.write(log::alevel::devel,"connection ping");
if (m_state != session::state::open) {
@@ -148,7 +148,7 @@ void connection<config>::ping(std::string const & payload, lib::error_code & ec)
return;
}
message_ptr msg = m_msg_manager->get_outgoing_message(frame::opcode::ping, payload.size());
message_ptr msg = m_msg_manager->get_message();
if (!msg) {
ec = error::make_error_code(error::no_outgoing_buffers);
return;
@@ -164,15 +164,17 @@ void connection<config>::ping(std::string const & payload, lib::error_code & ec)
m_ping_timer->cancel();
}
m_ping_timer = transport_con_type::set_timer(
config::timeout_pong,
lib::bind(
&type::handle_pong_timeout,
type::get_shared(),
payload,
lib::placeholders::_1
)
);
if (m_pong_timeout_dur > 0) {
m_ping_timer = transport_con_type::set_timer(
m_pong_timeout_dur,
lib::bind(
&type::handle_pong_timeout,
type::get_shared(),
payload,
lib::placeholders::_1
)
);
}
if (!m_ping_timer) {
// Our transport doesn't support timers
@@ -208,8 +210,8 @@ void connection<config>::ping(const std::string& payload) {
}
template<typename config>
void connection<config>::handle_pong_timeout(std::string payload,
lib::error_code const & ec)
void connection<config>::handle_pong_timeout(std::string payload, const lib::error_code &
ec)
{
if (ec) {
if (ec == transport::error::operation_aborted) {
@@ -227,8 +229,7 @@ void connection<config>::handle_pong_timeout(std::string payload,
}
template <typename config>
void connection<config>::pong(std::string const & payload, lib::error_code & ec)
{
void connection<config>::pong(const std::string& payload, lib::error_code& ec) {
m_alog.write(log::alevel::devel,"connection pong");
if (m_state != session::state::open) {
@@ -236,7 +237,7 @@ void connection<config>::pong(std::string const & payload, lib::error_code & ec)
return;
}
message_ptr msg = m_msg_manager->get_outgoing_message(frame::opcode::pong, payload.size());
message_ptr msg = m_msg_manager->get_message();
if (!msg) {
ec = error::make_error_code(error::no_outgoing_buffers);
return;
@@ -263,7 +264,7 @@ void connection<config>::pong(std::string const & payload, lib::error_code & ec)
}
template<typename config>
void connection<config>::pong(std::string const & payload) {
void connection<config>::pong(const std::string& payload) {
lib::error_code ec;
pong(payload,ec);
if (ec) {
@@ -291,7 +292,7 @@ void connection<config>::close(close::status::value const code,
template<typename config>
void connection<config>::close(close::status::value const code,
const std::string & reason)
std::string const & reason)
{
lib::error_code ec;
close(code,reason,ec);
@@ -323,6 +324,41 @@ void connection<config>::handle_interrupt() {
}
}
template <typename config>
lib::error_code connection<config>::pause_reading() {
m_alog.write(log::alevel::devel,"connection connection::pause_reading");
return transport_con_type::dispatch(
lib::bind(
&type::handle_pause_reading,
type::get_shared()
)
);
}
/// Pause reading handler. Not safe to call directly
template <typename config>
void connection<config>::handle_pause_reading() {
m_alog.write(log::alevel::devel,"connection connection::handle_pause_reading");
m_read_flag = false;
}
template <typename config>
lib::error_code connection<config>::resume_reading() {
m_alog.write(log::alevel::devel,"connection connection::resume_reading");
return transport_con_type::dispatch(
lib::bind(
&type::handle_resume_reading,
type::get_shared()
)
);
}
/// Resume reading helper method. Not safe to call directly
template <typename config>
void connection<config>::handle_resume_reading() {
m_read_flag = true;
read_frame();
}
@@ -648,14 +684,16 @@ template <typename config>
void connection<config>::read_handshake(size_t num_bytes) {
m_alog.write(log::alevel::devel,"connection read");
m_handshake_timer = transport_con_type::set_timer(
config::timeout_open_handshake,
lib::bind(
&type::handle_open_handshake_timeout,
type::get_shared(),
lib::placeholders::_1
)
);
if (m_open_handshake_timeout_dur > 0) {
m_handshake_timer = transport_con_type::set_timer(
m_open_handshake_timeout_dur,
lib::bind(
&type::handle_open_handshake_timeout,
type::get_shared(),
lib::placeholders::_1
)
);
}
transport_con_type::async_read_at_least(
num_bytes,
@@ -673,7 +711,7 @@ void connection<config>::read_handshake(size_t num_bytes) {
// All exit paths for this function need to call send_http_response() or submit
// a new read request with this function as the handler.
template <typename config>
void connection<config>::handle_read_handshake(const lib::error_code& ec,
void connection<config>::handle_read_handshake(lib::error_code const & ec,
size_t bytes_transferred)
{
m_alog.write(log::alevel::devel,"connection handle_read_handshake");
@@ -813,7 +851,7 @@ void connection<config>::send_http_response_error() {
// All exit paths for this function need to call send_http_response() or submit
// a new read request with this function as the handler.
template <typename config>
void connection<config>::handle_read_frame(const lib::error_code& ec,
void connection<config>::handle_read_frame(lib::error_code const & ec,
size_t bytes_transferred)
{
//m_alog.write(log::alevel::devel,"connection handle_read_frame");
@@ -824,6 +862,8 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
);
if (ec) {
log::level echannel = log::elevel::fatal;
if (ec == transport::error::eof) {
if (m_state == session::state::closed) {
// we expect to get eof if the connection is closed already
@@ -839,14 +879,19 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
}
}
if (ec == transport::error::tls_short_read) {
m_elog.write(log::elevel::rerror,"got TLS short read, killing connection for now");
this->terminate(ec);
return;
if (m_state == session::state::closed) {
// We expect to get a TLS short read if we try to read after the
// connection is closed. If this happens ignore and exit the
// read frame path.
terminate(lib::error_code());
return;
}
echannel = log::elevel::rerror;
} else if (ec == transport::error::action_after_shutdown) {
echannel = log::elevel::info;
}
std::stringstream s;
s << "error in handle_read_frame: " << ec.message() << " (" << ec << ")";
m_elog.write(log::elevel::fatal,s.str());
log_err(echannel, "handle_read_frame", ec);
this->terminate(ec);
return;
}
@@ -873,12 +918,12 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
m_alog.write(log::alevel::devel,s.str());
}
lib::error_code ec;
lib::error_code consume_ec;
p += m_processor->consume(
reinterpret_cast<uint8_t*>(m_buf)+p,
bytes_transferred-p,
ec
consume_ec
);
if (m_alog.static_test(log::alevel::devel)) {
@@ -886,20 +931,22 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
s << "bytes left after consume: " << bytes_transferred-p;
m_alog.write(log::alevel::devel,s.str());
}
if (ec) {
m_elog.write(log::elevel::rerror,"consume error: "+ec.message());
if (consume_ec) {
log_err(log::elevel::rerror, "consume", consume_ec);
if (config::drop_on_protocol_error) {
this->terminate(ec);
this->terminate(consume_ec);
return;
} else {
lib::error_code close_ec;
this->close(processor::error::to_ws(ec),ec.message(),close_ec);
this->close(
processor::error::to_ws(consume_ec),
consume_ec.message(),
close_ec
);
if (close_ec) {
m_elog.write(log::elevel::fatal,
"Failed to send a close frame after protocol error: "
+close_ec.message());
log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
this->terminate(close_ec);
return;
}
@@ -916,28 +963,31 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
message_ptr msg = m_processor->get_message();
// TODO: do we need to catch exceptions here and call the message
// handler hook. WebSocket++ doesn't throw any exceptions here, is
// there anything that could be thrown that could be recovered from?
if (!msg) {
m_alog.write(log::alevel::devel,
"null message from m_processor");
m_alog.write(log::alevel::devel, "null message from m_processor");
} else if (!is_control(msg->get_opcode())) {
// data message, dispatch to user
if (m_state != session::state::open) {
m_elog.write(log::elevel::warn,
"got non-close data frame in state closing");
m_elog.write(log::elevel::warn, "got non-close frame while closing");
} else if (m_message_handler) {
m_message_handler(m_connection_hdl, msg);
}
m_msg_manager->message_handler_hook(msg);
} else {
process_control_frame(msg);
m_msg_manager->message_handler_hook(msg);
}
}
}
read_frame();
}
/// Issue a new transport read unless reading is paused.
template <typename config>
void connection<config>::read_frame() {
if (!m_read_flag) {
return;
}
transport_con_type::async_read_at_least(
// std::min wont work with undefined static const values.
// TODO: is there a more elegant way to do this?
@@ -949,12 +999,6 @@ void connection<config>::handle_read_frame(const lib::error_code& ec,
1,
m_buf,
config::connection_read_buffer_size,
/*lib::bind(
&type::handle_read_frame,
type::get_shared(),
lib::placeholders::_1,
lib::placeholders::_2
)*/
m_handle_read_frame
);
}
@@ -971,8 +1015,7 @@ bool connection<config>::initialize_processor() {
int version = processor::get_websocket_version(m_request);
if (version < 0) {
m_alog.write(log::alevel::devel,
"BAD REQUEST: can't determine version");
m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
m_response.set_status(http::status_code::bad_request);
return false;
}
@@ -986,8 +1029,7 @@ bool connection<config>::initialize_processor() {
// We don't have a processor for this version. Return bad request
// with Sec-WebSocket-Version header filled with values we do accept
m_alog.write(log::alevel::devel,
"BAD REQUEST: no processor for version");
m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version");
m_response.set_status(http::status_code::bad_request);
std::stringstream ss;
@@ -1018,8 +1060,7 @@ bool connection<config>::process_handshake_request() {
);
if (!m_uri->get_valid()) {
m_alog.write(log::alevel::devel,
std::string("Bad request: failed to parse uri"));
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
m_response.set_status(http::status_code::bad_request);
return false;
}
@@ -1038,8 +1079,7 @@ bool connection<config>::process_handshake_request() {
// Validate: make sure all required elements are present.
if (ec){
// Not a valid handshake request
m_alog.write(log::alevel::devel,
"BAD REQUEST (724) "+ec.message());
m_alog.write(log::alevel::devel, "Bad request " + ec.message());
m_response.set_status(http::status_code::bad_request);
return false;
}
@@ -1052,8 +1092,7 @@ bool connection<config>::process_handshake_request() {
if (neg_results.first) {
// There was a fatal error in extension parsing that should result in
// a failed connection attempt.
m_alog.write(log::alevel::devel,
"BAD REQUEST: (737) " + neg_results.first.message());
m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
m_response.set_status(http::status_code::bad_request);
return false;
} else {
@@ -1071,8 +1110,7 @@ bool connection<config>::process_handshake_request() {
if (!m_uri->get_valid()) {
m_alog.write(log::alevel::devel,
std::string("Bad request: failed to parse uri"));
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
m_response.set_status(http::status_code::bad_request);
return false;
}
@@ -1096,14 +1134,14 @@ bool connection<config>::process_handshake_request() {
if (ec) {
std::stringstream s;
s << "Processing error: " << ec << "(" << ec.message() << ")";
m_alog.write(log::alevel::devel,s.str());
m_alog.write(log::alevel::devel, s.str());
m_response.set_status(http::status_code::internal_server_error);
return false;
}
} else {
// User application has rejected the handshake
m_alog.write(log::alevel::devel,"USER REJECT");
m_alog.write(log::alevel::devel, "USER REJECT");
// Use Bad Request if the user handler did not provide a more
// specific http response error code.
@@ -1166,9 +1204,7 @@ void connection<config>::send_http_response() {
}
template <typename config>
void connection<config>::handle_send_http_response(
const lib::error_code& ec)
{
void connection<config>::handle_send_http_response(lib::error_code const & ec) {
m_alog.write(log::alevel::devel,"handle_send_http_response");
this->atomic_state_check(
@@ -1177,8 +1213,7 @@ void connection<config>::handle_send_http_response(
);
if (ec) {
m_elog.write(log::elevel::rerror,
"error in handle_send_http_response: "+ec.message());
log_err(log::elevel::rerror,"handle_send_http_response",ec);
this->terminate(ec);
return;
}
@@ -1235,13 +1270,11 @@ void connection<config>::send_http_request() {
m_requested_subprotocols);
if (ec) {
m_elog.write(log::elevel::fatal,
"Internal library error: processor error: "+ec.message());
log_err(log::elevel::fatal,"Internal library error: Processor",ec);
return;
}
} else {
m_elog.write(log::elevel::fatal,
"Internal library error: missing processor");
m_elog.write(log::elevel::fatal,"Internal library error: missing processor");
return;
}
@@ -1257,18 +1290,19 @@ void connection<config>::send_http_request() {
m_handshake_buffer = m_request.raw();
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,
"Raw Handshake request:\n"+m_handshake_buffer);
m_alog.write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
}
m_handshake_timer = transport_con_type::set_timer(
config::timeout_open_handshake,
lib::bind(
&type::handle_open_handshake_timeout,
type::get_shared(),
lib::placeholders::_1
)
);
if (m_open_handshake_timeout_dur > 0) {
m_handshake_timer = transport_con_type::set_timer(
m_open_handshake_timeout_dur,
lib::bind(
&type::handle_open_handshake_timeout,
type::get_shared(),
lib::placeholders::_1
)
);
}
transport_con_type::async_write(
m_handshake_buffer.data(),
@@ -1282,7 +1316,7 @@ void connection<config>::send_http_request() {
}
template <typename config>
void connection<config>::handle_send_http_request(const lib::error_code& ec) {
void connection<config>::handle_send_http_request(lib::error_code const & ec) {
m_alog.write(log::alevel::devel,"handle_send_http_request");
this->atomic_state_check(
@@ -1291,8 +1325,7 @@ void connection<config>::handle_send_http_request(const lib::error_code& ec) {
);
if (ec) {
m_elog.write(log::elevel::rerror,
"error in handle_send_http_request: "+ec.message());
log_err(log::elevel::rerror,"handle_send_http_request",ec);
this->terminate(ec);
return;
}
@@ -1317,7 +1350,7 @@ void connection<config>::handle_send_http_request(const lib::error_code& ec) {
}
template <typename config>
void connection<config>::handle_read_http_response(const lib::error_code& ec,
void connection<config>::handle_read_http_response(lib::error_code const & ec,
size_t bytes_transferred)
{
m_alog.write(log::alevel::devel,"handle_read_http_response");
@@ -1328,8 +1361,7 @@ void connection<config>::handle_read_http_response(const lib::error_code& ec,
);
if (ec) {
m_elog.write(log::elevel::rerror,
"error in handle_read_http_response: "+ec.message());
log_err(log::elevel::rerror,"handle_send_http_response",ec);
this->terminate(ec);
return;
}
@@ -1352,16 +1384,13 @@ void connection<config>::handle_read_http_response(const lib::error_code& ec,
m_handshake_timer.reset();
}
lib::error_code ec = m_processor->validate_server_handshake_response(
lib::error_code validate_ec = m_processor->validate_server_handshake_response(
m_request,
m_response
);
if (ec) {
m_elog.write(log::elevel::rerror,
std::string("Server handshake response was invalid: ")+
ec.message()
);
this->terminate(ec);
if (validate_ec) {
log_err(log::elevel::rerror,"Server handshake response",validate_ec);
this->terminate(validate_ec);
return;
}
@@ -1407,14 +1436,13 @@ void connection<config>::handle_open_handshake_timeout(
lib::error_code const & ec)
{
if (ec == transport::error::operation_aborted) {
m_alog.write(log::alevel::devel,
"asio open handshake timer cancelled");
m_alog.write(log::alevel::devel,"open handshake timer cancelled");
} else if (ec) {
m_alog.write(log::alevel::devel,
"asio open handle_open_handshake_timeout error: "+ec.message());
"open handle_open_handshake_timeout error: "+ec.message());
// TODO: ignore or fail here?
} else {
m_alog.write(log::alevel::devel, "asio open handshake timer expired");
m_alog.write(log::alevel::devel,"open handshake timer expired");
terminate(make_error_code(error::open_handshake_timeout));
}
}
@@ -1424,8 +1452,7 @@ void connection<config>::handle_close_handshake_timeout(
lib::error_code const & ec)
{
if (ec == transport::error::operation_aborted) {
m_alog.write(log::alevel::devel,
"asio close handshake timer cancelled");
m_alog.write(log::alevel::devel,"asio close handshake timer cancelled");
} else if (ec) {
m_alog.write(log::alevel::devel,
"asio open handle_close_handshake_timeout error: "+ec.message());
@@ -1437,7 +1464,7 @@ void connection<config>::handle_close_handshake_timeout(
}
template <typename config>
void connection<config>::terminate(const lib::error_code & ec) {
void connection<config>::terminate(lib::error_code const & ec) {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"connection terminate");
}
@@ -1467,6 +1494,8 @@ void connection<config>::terminate(const lib::error_code & ec) {
return;
}
// TODO: choose between shutdown and close based on error code sent
transport_con_type::async_shutdown(
lib::bind(
&type::handle_terminate,
@@ -1479,7 +1508,7 @@ void connection<config>::terminate(const lib::error_code & ec) {
template <typename config>
void connection<config>::handle_terminate(terminate_status tstat,
const lib::error_code& ec)
lib::error_code const & ec)
{
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"connection handle_terminate");
@@ -1487,7 +1516,7 @@ void connection<config>::handle_terminate(terminate_status tstat,
if (ec) {
// there was an error actually shutting down the connection
m_elog.write(log::elevel::rerror,ec.message());
log_err(log::elevel::devel,"handle_terminate",ec);
}
// clean shutdown
@@ -1511,10 +1540,9 @@ void connection<config>::handle_terminate(terminate_status tstat,
if (m_termination_handler) {
try {
m_termination_handler(type::get_shared());
} catch (const std::exception& e) {
} catch (std::exception const & e) {
m_elog.write(log::elevel::warn,
std::string("termination_handler call failed. Reason was: ")
+e.what());
std::string("termination_handler call failed. Reason was: ")+e.what());
}
}
}
@@ -1534,52 +1562,80 @@ void connection<config>::write_frame() {
return;
}
// Get the next message in the queue. This will return an empty
// message if the queue was empty.
m_current_msg = write_pop();
if (!m_current_msg) {
// pull off all the messages that are ready to write.
// stop if we get a message marked terminal
message_ptr next_message = write_pop();
while (next_message) {
m_current_msgs.push_back(next_message);
if (!next_message->get_terminal()) {
next_message = write_pop();
} else {
next_message = message_ptr();
}
}
if (m_current_msgs.empty()) {
// there was nothing to send
return;
} else {
// At this point we own the next messages to be sent and are
// responsible for holding the write flag until they are
// successfully sent or there is some error
m_write_flag = true;
}
// At this point we own the next message to be sent and are
// responsible for holding the write flag until it is successfully
// sent or there is some error
m_write_flag = true;
}
std::string const & header = m_current_msg->get_header();
std::string const & payload = m_current_msg->get_payload();
m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
typename std::vector<message_ptr>::iterator it;
for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
std::string const & header = (*it)->get_header();
std::string const & payload = (*it)->get_payload();
m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
}
// Print detailed send stats if those log levels are enabled
if (m_alog.static_test(log::alevel::frame_header)) {
if (m_alog.dynamic_test(log::alevel::frame_header)) {
std::stringstream s;
s << "Dispatching write with " << header.size()
<< " header bytes and " << payload.size()
<< " payload bytes" << std::endl;
m_alog.write(log::alevel::frame_header,s.str());
m_alog.write(log::alevel::frame_header,"Header: "+utility::to_hex(header));
}
}
if (m_alog.static_test(log::alevel::frame_payload)) {
if (m_alog.dynamic_test(log::alevel::frame_payload)) {
m_alog.write(log::alevel::frame_payload,"Payload: "+utility::to_hex(payload));
}
}
std::stringstream general,header,payload;
general << "Dispatching write containing " << m_current_msgs.size()
<<" message(s) containing ";
header << "Header Bytes: \n";
payload << "Payload Bytes: \n";
size_t hbytes = 0;
size_t pbytes = 0;
for (size_t i = 0; i < m_current_msgs.size(); i++) {
hbytes += m_current_msgs[i]->get_header().size();
pbytes += m_current_msgs[i]->get_payload().size();
header << "[" << i << "] ("
<< m_current_msgs[i]->get_header().size() << ") "
<< utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
if (m_alog.static_test(log::alevel::frame_payload)) {
if (m_alog.dynamic_test(log::alevel::frame_payload)) {
payload << "[" << i << "] ("
<< m_current_msgs[i]->get_payload().size() << ") "
<< utility::to_hex(m_current_msgs[i]->get_payload())
<< "\n";
}
}
}
general << hbytes << " header bytes and " << pbytes << " payload bytes";
m_alog.write(log::alevel::frame_header,general.str());
m_alog.write(log::alevel::frame_header,header.str());
m_alog.write(log::alevel::frame_payload,payload.str());
}
}
transport_con_type::async_write(
m_send_buffer,
/*lib::bind(
&type::handle_write_frame,
type::get_shared(),
m_current_msg->get_terminal(),
lib::placeholders::_1
)*/
m_write_frame_handler
);
}
@@ -1591,19 +1647,19 @@ void connection<config>::handle_write_frame(lib::error_code const & ec)
m_alog.write(log::alevel::devel,"connection handle_write_frame");
}
bool terminate = m_current_msg->get_terminal();
bool terminal = m_current_msgs.back()->get_terminal();
m_send_buffer.clear();
m_msg_manager->write_handler_hook(m_current_msg);
m_current_msg.reset();
m_current_msgs.clear();
// TODO: recycle instead of deleting
if (ec) {
m_elog.write(log::elevel::fatal,"error in handle_write_frame: "+ec.message());
log_err(log::elevel::fatal,"handle_write_frame",ec);
this->terminate(ec);
return;
}
if (terminate) {
if (terminal) {
this->terminate(lib::error_code());
return;
}
@@ -1627,8 +1683,8 @@ void connection<config>::handle_write_frame(lib::error_code const & ec)
}
template <typename config>
void connection<config>::atomic_state_change(istate_type req,
istate_type dest, std::string msg)
void connection<config>::atomic_state_change(istate_type req, istate_type dest,
std::string msg)
{
scoped_lock_type lock(m_connection_state_lock);
@@ -1641,10 +1697,9 @@ void connection<config>::atomic_state_change(istate_type req,
}
template <typename config>
void connection<config>::atomic_state_change(
istate_type internal_req, istate_type internal_dest,
session::state::value external_req, session::state::value external_dest,
std::string msg)
void connection<config>::atomic_state_change(istate_type internal_req,
istate_type internal_dest, session::state::value external_req,
session::state::value external_dest, std::string msg)
{
scoped_lock_type lock(m_connection_state_lock);
@@ -1658,8 +1713,7 @@ void connection<config>::atomic_state_change(
}
template <typename config>
void connection<config>::atomic_state_check(istate_type req,
std::string msg)
void connection<config>::atomic_state_check(istate_type req, std::string msg)
{
scoped_lock_type lock(m_connection_state_lock);
@@ -1676,8 +1730,7 @@ const std::vector<int>& connection<config>::get_supported_versions() const
}
template <typename config>
void connection<config>::process_control_frame(typename
config::message_type::ptr msg)
void connection<config>::process_control_frame(typename config::message_type::ptr msg)
{
m_alog.write(log::alevel::devel,"process_control_frame");
@@ -1698,17 +1751,16 @@ void connection<config>::process_control_frame(typename
}
if (op == frame::opcode::PING) {
bool pong = true;
bool should_reply = true;
if (m_ping_handler) {
pong = m_ping_handler(m_connection_hdl, msg->get_payload());
should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
}
if (pong) {
if (should_reply) {
this->pong(msg->get_payload(),ec);
if (ec) {
m_elog.write(log::elevel::devel,
"Failed to send response pong: "+ec.message());
log_err(log::elevel::devel,"Failed to send response pong",ec);
}
}
} else if (op == frame::opcode::PONG) {
@@ -1724,7 +1776,7 @@ void connection<config>::process_control_frame(typename
m_remote_close_code = close::extract_code(msg->get_payload(),ec);
if (ec) {
std::stringstream s;
s.str("");
if (config::drop_on_protocol_error) {
s << "Received invalid close code " << m_remote_close_code
<< " dropping connection per config.";
@@ -1737,8 +1789,7 @@ void connection<config>::process_control_frame(typename
ec = send_close_ack(close::status::protocol_error,
"Invalid close code");
if (ec) {
m_elog.write(log::elevel::devel,
"send_close_ack error: "+ec.message());
log_err(log::elevel::devel,"send_close_ack",ec);
}
}
return;
@@ -1756,27 +1807,25 @@ void connection<config>::process_control_frame(typename
ec = send_close_ack(close::status::protocol_error,
"Invalid close reason");
if (ec) {
m_elog.write(log::elevel::devel,
"send_close_ack error: "+ec.message());
log_err(log::elevel::devel,"send_close_ack",ec);
}
}
return;
}
if (m_state == session::state::open) {
std::stringstream s;
s.str("");
s << "Received close frame with code " << m_remote_close_code
<< " and reason " << m_remote_close_reason;
m_alog.write(log::alevel::devel,s.str());
ec = send_close_ack();
if (ec) {
m_elog.write(log::elevel::devel,
"send_close_ack error: "+ec.message());
log_err(log::elevel::devel,"send_close_ack",ec);
}
} else if (m_state == session::state::closing && !m_was_clean) {
// ack of our close
m_alog.write(log::alevel::devel,"Got acknowledgement of close");
m_alog.write(log::alevel::devel, "Got acknowledgement of close");
m_was_clean = true;
@@ -1792,27 +1841,30 @@ void connection<config>::process_control_frame(typename
}
} else {
// spurious, ignore
m_elog.write(log::elevel::devel,"Got close frame in wrong state");
m_elog.write(log::elevel::devel, "Got close frame in wrong state");
}
} else {
// got an invalid control opcode
m_elog.write(log::elevel::devel,"Got control frame with invalid opcode");
m_elog.write(log::elevel::devel, "Got control frame with invalid opcode");
// initiate protocol error shutdown
}
}
template <typename config>
lib::error_code connection<config>::send_close_ack(close::status::value code,
const std::string &reason)
std::string const & reason)
{
return send_close_frame(code,reason,true,m_is_server);
}
template <typename config>
lib::error_code connection<config>::send_close_frame(close::status::value code,
const std::string &reason, bool ack, bool terminal)
std::string const & reason, bool ack, bool terminal)
{
m_alog.write(log::alevel::devel,"send_close_frame");
// check for special codes
// If silent close is set, respect it and blank out close information
// Otherwise use whatever has been specified in the parameters. If
// parameters specifies close::status::blank then determine what to do
@@ -1847,7 +1899,7 @@ lib::error_code connection<config>::send_close_frame(close::status::value code,
<< m_local_close_reason;
m_alog.write(log::alevel::devel,s.str());
message_ptr msg = m_msg_manager->get_outgoing_message(frame::opcode::close,m_local_close_reason.size());
message_ptr msg = m_msg_manager->get_message();
if (!msg) {
return error::make_error_code(error::no_outgoing_buffers);
}
@@ -1873,14 +1925,16 @@ lib::error_code connection<config>::send_close_frame(close::status::value code,
// Start a timer so we don't wait forever for the acknowledgement close
// frame
m_handshake_timer = transport_con_type::set_timer(
config::timeout_close_handshake,
lib::bind(
&type::handle_close_handshake_timeout,
type::get_shared(),
lib::placeholders::_1
)
);
if (m_close_handshake_timeout_dur > 0) {
m_handshake_timer = transport_con_type::set_timer(
m_close_handshake_timeout_dur,
lib::bind(
&type::handle_close_handshake_timeout,
type::get_shared(),
lib::placeholders::_1
)
);
}
bool needs_writing = false;
{
@@ -1903,49 +1957,49 @@ template <typename config>
typename connection<config>::processor_ptr
connection<config>::get_processor(int version) const {
// TODO: allow disabling certain versions
processor_ptr p;
switch (version) {
case 0:
return processor_ptr(
new processor::hybi00<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager
)
);
p.reset(new processor::hybi00<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager
));
break;
case 7:
return processor_ptr(
new processor::hybi07<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager,
m_rng
)
);
p.reset(new processor::hybi07<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager,
m_rng
));
break;
case 8:
return processor_ptr(
new processor::hybi08<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager,
m_rng
)
);
p.reset(new processor::hybi08<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager,
m_rng
));
break;
case 13:
return processor_ptr(
new processor::hybi13<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager,
m_rng
)
);
p.reset(new processor::hybi13<config>(
transport_con_type::is_secure(),
m_is_server,
m_msg_manager,
m_rng
));
break;
default:
return processor_ptr();
return p;
}
// Settings not configured by the constructor
p->set_max_message_size(m_max_message_size);
return p;
}
template <typename config>
+57 -12
View File
@@ -40,6 +40,7 @@ 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));
@@ -64,6 +65,19 @@ 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);
@@ -76,8 +90,7 @@ 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;}
@@ -95,8 +108,42 @@ void endpoint<connection,config>::interrupt(connection_hdl hdl) {
}
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>::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)
{
connection_ptr con = get_con_from_hdl(hdl,ec);
if (ec) {return;}
@@ -105,8 +152,8 @@ void endpoint<connection,config>::send(connection_hdl hdl, std::string const &
}
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);
@@ -176,8 +223,7 @@ 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);
@@ -185,8 +231,8 @@ void endpoint<connection,config>::ping(connection_hdl hdl, std::string const &
}
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;}
@@ -194,8 +240,7 @@ void endpoint<connection,config>::pong(connection_hdl hdl, std::string const &
}
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);
-24
View File
@@ -67,30 +67,6 @@ 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
-160
View File
@@ -1,160 +0,0 @@
/*
* 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
@@ -1,321 +0,0 @@
/*
* 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
+4 -3
View File
@@ -34,6 +34,7 @@
#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>
@@ -57,8 +58,8 @@ public:
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
explicit hybi00(bool secure, bool server, msg_manager_ptr manager)
: processor<config>(secure, server)
explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager)
: processor<config>(secure, p_is_server)
, msg_hdr(0x00)
, msg_ftr(0xff)
, m_state(HEADER)
@@ -217,7 +218,7 @@ public:
if (m_state == HEADER) {
if (buf[p] == msg_hdr) {
p++;
m_msg_ptr = m_msg_manager->get_incoming_message(frame::opcode::text,1);
m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1);
if (!m_msg_ptr) {
ec = make_error_code(websocketpp::error::no_incoming_buffers);
+2 -3
View File
@@ -45,9 +45,8 @@ public:
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
typedef typename config::rng_type rng_type;
explicit hybi07(bool secure, bool server, msg_manager_ptr manager,
rng_type& rng)
: hybi08<config>(secure, server, manager, rng) {}
explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: hybi08<config>(secure, p_is_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,
+2 -3
View File
@@ -46,9 +46,8 @@ public:
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
typedef typename config::rng_type rng_type;
explicit hybi08(bool secure, bool server, msg_manager_ptr manager,
rng_type& rng)
: hybi13<config>(secure, server, manager, rng) {}
explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: hybi13<config>(secure, p_is_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,
+21 -6
View File
@@ -33,6 +33,7 @@
#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>
@@ -67,9 +68,8 @@ public:
typedef std::pair<lib::error_code,std::string> err_str_pair;
explicit hybi13(bool secure, bool server, msg_manager_ptr manager,
rng_type& rng)
: processor<config>(secure,server)
explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: processor<config>(secure, p_is_server)
, m_msg_manager(manager)
, m_rng(rng)
{
@@ -362,18 +362,32 @@ public:
if (frame::opcode::is_control(op)) {
m_control_msg = msg_metadata(
m_msg_manager->get_incoming_message(op,m_bytes_needed),
m_msg_manager->get_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_incoming_message(op,m_bytes_needed),
m_msg_manager->get_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(
@@ -382,7 +396,8 @@ public:
m_extended_header
)
);
// TODO: reserve space in the existing message for the new bytes
out.reserve(out.size() + m_bytes_needed);
}
m_current_msg = &m_data_msg;
}
+40 -12
View File
@@ -159,15 +159,45 @@ public:
typedef typename config::message_type::ptr message_ptr;
typedef std::pair<lib::error_code,std::string> err_str_pair;
explicit processor(bool secure, bool server)
explicit processor(bool secure, bool p_is_server)
: m_secure(secure)
, m_server(server) {}
, m_server(p_is_server)
, m_max_message_size(config::max_message_size)
{}
virtual ~processor() {}
/// Get the protocol version of this processor
virtual int get_version() const = 0;
/// 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
@@ -196,8 +226,7 @@ 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
/**
@@ -236,8 +265,7 @@ 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
/**
@@ -310,8 +338,7 @@ 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
/**
@@ -324,8 +351,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
/**
@@ -338,8 +365,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
/**
@@ -361,6 +388,7 @@ public:
protected:
bool const m_secure;
bool const m_server;
size_t m_max_message_size;
};
} // namespace processor
+74 -25
View File
@@ -64,52 +64,101 @@ 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");
}
// return an initialized connection_ptr. Call start() on this object to
// begin the processing loop.
/// 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.
*/
connection_ptr get_connection() {
connection_ptr con = endpoint_type::create_connection();
return con;
return endpoint_type::create_connection();
}
// Starts the server's async connection acceptance loop.
void start_accept() {
/// 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();
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
)
lib::bind(&type::handle_accept,this,con,lib::placeholders::_1),
ec
);
if (ec && con) {
// If the connection was constructed but the accept failed,
// terminate the connection to prevent memory leaks
con->terminate(lib::error_code());
}
}
void handle_accept(connection_ptr con, const lib::error_code& ec) {
/// 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) {
if (ec) {
con->terminate(ec);
endpoint_type::m_elog.write(log::elevel::rerror,
"handle_accept error: "+ec.message());
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());
}
} else {
con->start();
}
// TODO: are there cases where we should terminate this loop?
start_accept();
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());
}
}
private:
};
} // namespace websocketpp
+134 -70
View File
@@ -28,6 +28,7 @@
#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>
@@ -123,7 +124,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
@@ -407,7 +408,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
@@ -415,35 +416,37 @@ 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 {
// 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_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);
}
return socket_con_type::init_asio(io_service, m_strand, m_is_server);
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;
}
void handle_pre_init(lib::error_code const & ec) {
@@ -497,7 +500,7 @@ protected:
}
void handle_post_init_timeout(timer_ptr post_timer, init_handler callback,
const lib::error_code& ec)
lib::error_code const & ec)
{
lib::error_code ret_ec;
@@ -523,8 +526,8 @@ protected:
callback(ret_ec);
}
void handle_post_init(timer_ptr post_timer, init_handler callback, const
lib::error_code& ec)
void handle_post_init(timer_ptr post_timer, init_handler callback,
lib::error_code const & ec)
{
if (ec == transport::error::operation_aborted ||
post_timer->expires_from_now().is_negative())
@@ -539,7 +542,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);
}
@@ -621,7 +624,8 @@ 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();
@@ -688,7 +692,8 @@ 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.
@@ -774,6 +779,13 @@ 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) {
@@ -786,62 +798,86 @@ 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),
/*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)
make_custom_alloc_handler(
m_read_handler_allocator,
m_async_read_handler
)
);
}
void handle_async_read(const boost::system::error_code& ec,
void handle_async_read(boost::system::error_code const & ec,
size_t bytes_transferred)
{
if (!ec) {
m_read_handler(lib::error_code(), bytes_transferred);
return;
}
m_alog.write(log::alevel::devel, "asio con handle_async_read");
// translate boost error codes into more lib::error_codes
lib::error_code tec;
if (ec == boost::asio::error::eof) {
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);
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_;
} else {
log_err(log::elevel::info,"asio async_read_at_least",ec);
m_read_handler(make_error_code(transport::error::pass_through),
bytes_transferred);
// 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");
}
}
void async_write(const char* buf, size_t len, write_handler handler) {
m_bufs.push_back(boost::asio::buffer(buf,len));
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_write_handler = handler;
boost::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
/*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)
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) {
@@ -853,13 +889,10 @@ protected:
boost::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
/*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)
make_custom_alloc_handler(
m_write_handler_allocator,
m_async_write_handler
)
);
}
@@ -867,11 +900,20 @@ protected:
size_t bytes_transferred)
{
m_bufs.clear();
lib::error_code tec;
if (ec) {
log_err(log::elevel::info,"asio async_write",ec);
m_write_handler(make_error_code(transport::error::pass_through));
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_;
} else {
m_write_handler(lib::error_code());
// 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");
}
}
@@ -919,6 +961,16 @@ 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,
@@ -943,7 +995,7 @@ protected:
}
void handle_async_shutdown_timeout(timer_ptr shutdown_timer, init_handler
callback, const lib::error_code& ec)
callback, lib::error_code const & ec)
{
lib::error_code ret_ec;
@@ -954,7 +1006,7 @@ protected:
return;
}
log_err(log::elevel::devel,"asio handle_async_socket_shutdown",ec);
log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
ret_ec = ec;
} else {
ret_ec = make_error_code(transport::error::timeout);
@@ -967,7 +1019,7 @@ protected:
}
void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
callback, const boost::system::error_code & ec)
callback, boost::system::error_code const & ec)
{
if (ec == boost::asio::error::operation_aborted ||
shutdown_timer->expires_from_now().is_negative())
@@ -978,25 +1030,37 @@ 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 {
callback(make_error_code(transport::error::pass_through));
// 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);
}
}
} 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
@@ -1041,7 +1105,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;
+160 -67
View File
@@ -84,10 +84,14 @@ 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;
@@ -119,6 +123,8 @@ 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;
@@ -132,11 +138,14 @@ 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;
@@ -216,6 +225,90 @@ 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
@@ -231,58 +324,6 @@ 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
@@ -303,12 +344,25 @@ public:
m_alog->write(log::alevel::devel,"asio::listen");
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();
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();
}
}
/// Set up endpoint for listening manually
@@ -494,6 +548,14 @@ 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();
@@ -532,6 +594,34 @@ 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
@@ -601,10 +691,8 @@ 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::invalid_state);
ec = make_error_code(websocketpp::error::async_accept_not_listening);
return;
}
@@ -669,8 +757,12 @@ protected:
m_alog->write(log::alevel::devel, "asio::handle_accept");
if (boost_ec) {
log_err(log::elevel::devel,"asio handle_accept",boost_ec);
ret_ec = make_error_code(error::pass_through);
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);
}
}
callback(ret_ec);
@@ -910,10 +1002,6 @@ 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.
@@ -966,6 +1054,11 @@ 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;
+7 -2
View File
@@ -95,7 +95,10 @@ namespace error {
pass_through,
/// Required tls_init handler not present
missing_tls_init_handler
missing_tls_init_handler,
/// TLS Handshake Failed
tls_handshake_failed,
};
} // namespace error
@@ -119,9 +122,11 @@ public:
case error::tls_handshake_timeout:
return "TLS handshake timed out";
case error::pass_through:
return "Pass through from underlying library";
return "Pass through from socket policy";
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,6 +229,23 @@ 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,
+33 -3
View File
@@ -260,11 +260,10 @@ protected:
m_hdl = hdl;
}
void handle_init(init_handler callback, const
boost::system::error_code& ec)
void handle_init(init_handler callback,boost::system::error_code const & ec)
{
if (ec) {
m_ec = socket::make_error_code(socket::error::pass_through);
m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
} else {
m_ec = lib::error_code();
}
@@ -284,6 +283,37 @@ 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) {
+11 -1
View File
@@ -169,7 +169,13 @@ enum value {
tls_short_read,
/// Timer expired
timeout
timeout,
/// read or write after shutdown
action_after_shutdown,
/// Other TLS error
tls_error,
};
class category : public lib::error_category {
@@ -198,6 +204,10 @@ 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,8 +28,9 @@
#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/connection.hpp>
+12 -12
View File
@@ -47,13 +47,13 @@ static uint16_t const uri_default_secure_port = 443;
class uri {
public:
explicit uri(std::string const & uri) : m_valid(false) {
explicit uri(std::string const & uri_string) : m_valid(false) {
std::string::const_iterator it;
std::string::const_iterator temp;
int state = 0;
it = uri.begin();
it = uri_string.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.end()) {
while (temp != uri_string.end()) {
if (*temp == ']') {
break;
}
++temp;
}
if (temp == uri.end()) {
if (temp == uri_string.end()) {
return;
} else {
// validate IPv6 literal parts
@@ -103,7 +103,7 @@ public:
m_host.append(it,temp);
}
it = temp+1;
if (it == uri.end()) {
if (it == uri_string.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.end()) {
if (it == uri_string.end()) {
state = 2;
break;
} else if (*it == '/') {
@@ -137,7 +137,7 @@ public:
// parse port
std::string port = "";
while (state == 1) {
if (it == uri.end()) {
if (it == uri_string.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.end());
m_resource.append(it,uri_string.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 "";
}
}
+1 -1
View File
@@ -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 std::tolower (c1) < std::tolower (c2);
return tolower (c1) < tolower (c2);
}
};
bool operator() (std::string const & s1, std::string const & s2) const {