diff --git a/SConstruct b/SConstruct index b064417..8761ee9 100644 --- a/SConstruct +++ b/SConstruct @@ -34,6 +34,11 @@ elif os.environ.has_key('BOOST_INCLUDES') and os.environ.has_key('BOOST_LIBS'): else: raise SCons.Errors.UserError, "Neither BOOST_ROOT, nor BOOST_INCLUDES + BOOST_LIBS was set!" +## Custom OpenSSL +if os.environ.has_key('OPENSSL_PATH'): + env.Append(CPPPATH = os.path.join(os.environ['OPENSSL_PATH'], 'include')) + env.Append(LIBPATH = os.environ['OPENSSL_PATH']) + if os.environ.has_key('WSPP_ENABLE_CPP11'): env['WSPP_ENABLE_CPP11'] = True else: @@ -93,8 +98,7 @@ if env['PLATFORM'].startswith('win'): #env['LIBPATH'] = env['BOOST_LIBS'] pass else: - env['LIBPATH'] = ['/usr/lib', - '/usr/local/lib'] #, env['BOOST_LIBS'] + env.Append(LIBPATH = ['/usr/lib', '/usr/local/lib']) # Compiler specific warning flags if env['CXX'].startswith('g++'): @@ -260,6 +264,9 @@ subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',varia # telemetry_server telemetry_server = SConscript('#/examples/telemetry_server/SConscript',variant_dir = builddir + 'telemetry_server',duplicate = 0) +# external_io_service +external_io_service = SConscript('#/examples/external_io_service/SConscript',variant_dir = builddir + 'external_io_service',duplicate = 0) + if not env['PLATFORM'].startswith('win'): # iostream_server iostream_server = SConscript('#/examples/iostream_server/SConscript',variant_dir = builddir + 'iostream_server',duplicate = 0) diff --git a/changelog.md b/changelog.md index 30cc5b2..75029c2 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,7 @@ HEAD `pass_through` error type. - Improvement: Add a `get_transport_error` method to Asio transport connections to allow retrieving a machine readable native transport error. +- Documentation: Adds an example demonstrating the use of external `io_service` - Bug: Fix memory leak when init_asio produces an error. #454 Thank you Mark Grimes for reporting and fixing. - Bug: Fix crash when processing a specially crafted HTTP header. Thank you Eli diff --git a/examples/external_io_service/CMakeLists.txt b/examples/external_io_service/CMakeLists.txt new file mode 100644 index 0000000..5223da1 --- /dev/null +++ b/examples/external_io_service/CMakeLists.txt @@ -0,0 +1,12 @@ + +file (GLOB SOURCE_FILES *.cpp) +file (GLOB HEADER_FILES *.hpp) + +init_target (external_io_service) + +build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES}) + +link_boost () +final_target () + +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "examples") diff --git a/examples/external_io_service/SConscript b/examples/external_io_service/SConscript new file mode 100644 index 0000000..beb7c1c --- /dev/null +++ b/examples/external_io_service/SConscript @@ -0,0 +1,23 @@ +## Main development example +## + +Import('env') +Import('env_cpp11') +Import('boostlibs') +Import('platform_libs') +Import('polyfill_libs') + +env = env.Clone () +env_cpp11 = env_cpp11.Clone () + +prgs = [] + +# if a C++11 environment is 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('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS) +else: + ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] + prgs += env.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS) + +Return('prgs') diff --git a/examples/external_io_service/external_io_service.cpp b/examples/external_io_service/external_io_service.cpp new file mode 100644 index 0000000..b6ce3b1 --- /dev/null +++ b/examples/external_io_service/external_io_service.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 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 "tcp_echo_server.hpp" + +#include +#include + +#include + +using websocketpp::lib::placeholders::_1; +using websocketpp::lib::placeholders::_2; +using websocketpp::lib::bind; + +typedef websocketpp::server ws_echo_server; + +// Define a callback to handle incoming messages +void on_message(ws_echo_server* s, websocketpp::connection_hdl hdl, ws_echo_server::message_ptr msg) { + std::cout << "on_message called with hdl: " << hdl.lock().get() + << " and message: " << msg->get_payload() + << std::endl; + + // check for a special command to instruct the server to stop listening so + // it can be cleanly exited. + if (msg->get_payload() == "stop-listening") { + s->stop_listening(); + return; + } + + try { + s->send(hdl, msg->get_payload(), msg->get_opcode()); + } catch (websocketpp::lib::error_code const & e) { + std::cout << "Echo failed because: " << e + << "(" << e.message() << ")" << std::endl; + } +} + +int main() { + asio::io_service service; + + // Add a TCP echo server on port 9003 + tcp_echo_server custom_http_server(service, 9003); + + // Add a WebSocket echo server on port 9002 + ws_echo_server ws_server; + ws_server.set_access_channels(websocketpp::log::alevel::all); + ws_server.clear_access_channels(websocketpp::log::alevel::frame_payload); + + // The only difference in this code between an internal and external + // io_service is the different constructor to init_asio + ws_server.init_asio(&service); + + // Register our message handler + ws_server.set_message_handler(bind(&on_message,&ws_server,::_1,::_2)); + ws_server.listen(9002); + ws_server.start_accept(); + + // TODO: add a timer? + + // Start the Asio io_service run loop for all + service.run(); +} \ No newline at end of file diff --git a/examples/external_io_service/tcp_echo_server.hpp b/examples/external_io_service/tcp_echo_server.hpp new file mode 100644 index 0000000..f6cd618 --- /dev/null +++ b/examples/external_io_service/tcp_echo_server.hpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * TCP Echo Server + * + * This file defines a simple TCP Echo Server. It is adapted from the Asio + * example: cpp03/echo/async_tcp_echo_server.cpp + */ + +#include +#include +#include + +using websocketpp::lib::placeholders::_1; +using websocketpp::lib::placeholders::_2; +using websocketpp::lib::bind; + +namespace asio = websocketpp::lib::asio; +using tcp = asio::ip::tcp; + +struct tcp_echo_session : websocketpp::lib::enable_shared_from_this { + typedef websocketpp::lib::shared_ptr ptr; + + tcp_echo_session(asio::io_service & service) : m_socket(service) {} + + void start() { + m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)), + websocketpp::lib::bind( + &tcp_echo_session::handle_read, shared_from_this(), _1, _2)); + } + + void handle_read(const asio::error_code & ec, size_t transferred) { + if (!ec) { + asio::async_write(m_socket, + asio::buffer(m_buffer, transferred), + bind(&tcp_echo_session::handle_write, shared_from_this(), _1)); + } + } + + void handle_write(const asio::error_code & ec) { + if (!ec) { + m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)), + bind(&tcp_echo_session::handle_read, shared_from_this(), _1, _2)); + } + } + + tcp::socket m_socket; + char m_buffer[1024]; +}; + +struct tcp_echo_server { + tcp_echo_server(asio::io_service & service, short port) + : m_service(service) + , m_acceptor(service, tcp::endpoint(tcp::v6(), port)) + { + this->start_accept(); + } + + void start_accept() { + tcp_echo_session::ptr new_session(new tcp_echo_session(m_service)); + m_acceptor.async_accept(new_session->m_socket, + bind(&tcp_echo_server::handle_accept, this, new_session, _1)); + } + + void handle_accept(tcp_echo_session::ptr new_session, const asio::error_code & ec) { + if (!ec) { + new_session->start(); + } + start_accept(); + } + + asio::io_service & m_service; + tcp::acceptor m_acceptor; +};