Merge branch 'master' into tutorials
This commit is contained in:
commit
bded14b635
22 files changed
+472
-135
No files matched your search
@@ -146,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
|
||||
@@ -229,6 +230,9 @@ utility_client = SConscript('#/examples/utility_client/SConscript',variant_dir =
|
||||
# debug_client
|
||||
debug_client = SConscript('#/examples/debug_client/SConscript',variant_dir = builddir + 'debug_client',duplicate = 0)
|
||||
|
||||
# debug_server
|
||||
debug_server = SConscript('#/examples/debug_server/SConscript',variant_dir = builddir + 'debug_server',duplicate = 0)
|
||||
|
||||
# subprotocol_server
|
||||
subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',variant_dir = builddir + 'subprotocol_server',duplicate = 0)
|
||||
|
||||
|
||||
+8
-1
@@ -12,7 +12,7 @@ HEAD
|
||||
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
|
||||
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
|
||||
@@ -30,6 +30,10 @@ HEAD
|
||||
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.
|
||||
@@ -48,6 +52,9 @@ HEAD
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,30 @@
|
||||
/**
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -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 ()
|
||||
@@ -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')
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,9 @@
|
||||
|
||||
#include <websocketpp/config/core.hpp>
|
||||
#include <websocketpp/config/core_client.hpp>
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/config/asio_no_tls_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>
|
||||
|
||||
@@ -79,9 +80,54 @@ 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;
|
||||
|
||||
@@ -554,3 +600,7 @@ BOOST_AUTO_TEST_CASE( pause_reading ) {
|
||||
BOOST_CHECK_EQUAL( con->read_some(buffer+1, 1), 1);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( server_connection_cleanup ) {
|
||||
server_tls s;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#define WEBSOCKETPP_COMMON_NETWORK_HPP
|
||||
|
||||
// For ntohs and htons
|
||||
#if defined(WIN32)
|
||||
#if defined(_WIN32)
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
//#include <arpa/inet.h>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* don't fit somewhere else better.
|
||||
*/
|
||||
|
||||
#if defined(WIN32) && !defined(NOMINMAX)
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
// don't define min and max macros that conflict with std::min and std::max
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#if WIN32 && (_MSC_VER < 1600)
|
||||
#if defined (_WIN32) && defined (_MSC_VER) && (_MSC_VER < 1600)
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
using boost::int8_t;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
@@ -1451,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
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,7 @@ inline size_t request::consume(const char *buf, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
begin = end+sizeof(header_delimiter)-1;
|
||||
begin = end+(sizeof(header_delimiter)-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -1204,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(
|
||||
@@ -1318,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(
|
||||
@@ -1438,13 +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));
|
||||
}
|
||||
}
|
||||
@@ -1542,7 +1540,7 @@ 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());
|
||||
}
|
||||
@@ -1564,43 +1562,77 @@ 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,
|
||||
@@ -1615,10 +1647,11 @@ void connection<config>::handle_write_frame(lib::error_code const & ec)
|
||||
m_alog.write(log::alevel::devel,"connection handle_write_frame");
|
||||
}
|
||||
|
||||
bool terminal = m_current_msg->get_terminal();
|
||||
bool terminal = m_current_msgs.back()->get_terminal();
|
||||
|
||||
m_send_buffer.clear();
|
||||
m_current_msg.reset();
|
||||
m_current_msgs.clear();
|
||||
// TODO: recycle instead of deleting
|
||||
|
||||
if (ec) {
|
||||
log_err(log::elevel::fatal,"handle_write_frame",ec);
|
||||
|
||||
@@ -64,48 +64,76 @@ 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");
|
||||
}
|
||||
|
||||
// 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() {
|
||||
connection_ptr con = get_connection();
|
||||
/// 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;
|
||||
}
|
||||
|
||||
lib::error_code ec;
|
||||
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 == 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 (ec) {
|
||||
endpoint_type::m_elog.write(log::elevel::rerror,
|
||||
"start_accept error: "+ec.message());
|
||||
if (ec && con) {
|
||||
// If the connection was constructed but the accept failed,
|
||||
// terminate the connection to prevent memory leaks
|
||||
con->terminate(lib::error_code());
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the server's async connection acceptance loop
|
||||
/**
|
||||
* Initiates the server connection acceptance loop. Must be called after
|
||||
* listen. This method will have no effect until the underlying io_service
|
||||
* starts running. It may be called after the io_service is already running.
|
||||
*
|
||||
* Refer to documentation for the transport policy you are using for
|
||||
* instructions on how to stop this acceptance loop.
|
||||
*/
|
||||
void start_accept() {
|
||||
lib::error_code ec;
|
||||
start_accept(ec);
|
||||
if (ec) {
|
||||
throw ec;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler callback for start_accept
|
||||
void handle_accept(connection_ptr con, lib::error_code const & ec) {
|
||||
if (ec) {
|
||||
con->terminate(ec);
|
||||
@@ -121,10 +149,16 @@ public:
|
||||
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
|
||||
|
||||
@@ -408,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
|
||||
@@ -416,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) {
|
||||
@@ -498,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;
|
||||
|
||||
@@ -524,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())
|
||||
@@ -796,6 +798,11 @@ 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),
|
||||
@@ -810,6 +817,8 @@ protected:
|
||||
void handle_async_read(boost::system::error_code const & ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
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) {
|
||||
@@ -818,8 +827,8 @@ protected:
|
||||
// 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 ||
|
||||
|
||||
if (tec == transport::error::tls_error ||
|
||||
tec == transport::error::pass_through)
|
||||
{
|
||||
// These are aggregate/catch all errors. Log some human readable
|
||||
@@ -828,7 +837,16 @@ protected:
|
||||
log_err(log::elevel::info,"asio async_read_at_least",ec);
|
||||
}
|
||||
}
|
||||
m_read_handler(tec,bytes_transferred);
|
||||
if (m_read_handler) {
|
||||
m_read_handler(tec,bytes_transferred);
|
||||
// TODO: why does this line break things?
|
||||
//m_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
|
||||
} else {
|
||||
// This can happen in cases where the connection is terminated while
|
||||
// the transport is waiting on a read.
|
||||
m_alog.write(log::alevel::devel,
|
||||
"handle_async_read called with null read handler");
|
||||
}
|
||||
}
|
||||
|
||||
void async_write(const char* buf, size_t len, write_handler handler) {
|
||||
@@ -882,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -941,6 +968,9 @@ protected:
|
||||
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,
|
||||
@@ -965,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;
|
||||
|
||||
@@ -976,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);
|
||||
@@ -989,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())
|
||||
@@ -1011,10 +1041,10 @@ protected:
|
||||
// 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
|
||||
// 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 {
|
||||
|
||||
@@ -123,7 +123,7 @@ 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(0)
|
||||
, m_listen_backlog(boost::asio::socket_base::max_connections)
|
||||
, m_reuse_addr(src.m_reuse_addr)
|
||||
, m_state(src.m_state)
|
||||
{
|
||||
@@ -145,7 +145,7 @@ public:
|
||||
rhs.m_io_service = NULL;
|
||||
rhs.m_external_io_service = false;
|
||||
rhs.m_acceptor = NULL;
|
||||
rhs.m_listen_backlog = 0;
|
||||
rhs.m_listen_backlog = boost::asio::socket_base::max_connections;
|
||||
rhs.m_state = UNINITIALIZED;
|
||||
}
|
||||
return *this;
|
||||
@@ -344,16 +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(m_reuse_addr));
|
||||
m_acceptor->bind(ep);
|
||||
if (m_listen_backlog == 0) {
|
||||
m_acceptor->listen();
|
||||
boost::system::error_code bec;
|
||||
|
||||
m_acceptor->open(ep.protocol(),bec);
|
||||
if (!bec) {
|
||||
m_acceptor->set_option(boost::asio::socket_base::reuse_address(m_reuse_addr),bec);
|
||||
}
|
||||
if (!bec) {
|
||||
m_acceptor->bind(ep,bec);
|
||||
}
|
||||
if (!bec) {
|
||||
m_acceptor->listen(m_listen_backlog,bec);
|
||||
}
|
||||
if (bec) {
|
||||
log_err(log::elevel::info,"asio listen",bec);
|
||||
ec = make_error_code(error::pass_through);
|
||||
} else {
|
||||
m_acceptor->listen(m_listen_backlog);
|
||||
m_state = LISTENING;
|
||||
ec = lib::error_code();
|
||||
}
|
||||
m_state = LISTENING;
|
||||
ec = lib::error_code();
|
||||
}
|
||||
|
||||
/// Set up endpoint for listening manually
|
||||
@@ -539,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();
|
||||
@@ -985,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.
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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,7 +283,7 @@ 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
|
||||
|
||||
+4
-4
@@ -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 "";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user