diff --git a/SConstruct b/SConstruct index c9a6f8f85b..2873c125de 100644 --- a/SConstruct +++ b/SConstruct @@ -2,10 +2,12 @@ import os, sys, commands env = Environment(ENV = os.environ) # figure out a better way to configure this -#env["CXX"] = "clang++" if os.environ.has_key('CXX'): env['CXX'] = os.environ['CXX'] +if os.environ.has_key('DEBUG'): + env['DEBUG'] = os.environ['DEBUG'] + if os.environ.has_key('CXXFLAGS'): #env['CXXFLAGS'] = os.environ['CXXFLAGS'] env.Append(CXXFLAGS = os.environ['CXXFLAGS']) @@ -69,14 +71,20 @@ if env['PLATFORM'].startswith('win'): env['CCFLAGS'] = '%s /EHsc /GR /GS- /MD /nologo %s %s' % (warn_flags, arch_flags, opt_flags) env['LINKFLAGS'] = '/INCREMENTAL:NO /MANIFEST /NOLOGO /OPT:REF /OPT:ICF /MACHINE:X86' elif env['PLATFORM'] == 'posix': - env.Append(CPPDEFINES = ['NDEBUG']) + if env.has_key('DEBUG'): + env.Append(CCFLAGS = ['-g', '-O0']) + else: + env.Append(CPPDEFINES = ['NDEBUG']) + env.Append(CCFLAGS = ['-O3', '-fomit-frame-pointer']) env.Append(CCFLAGS = ['-Wall']) - #env.Append(CCFLAGS = ['-O3', '-fomit-frame-pointer']) #env['LINKFLAGS'] = '' elif env['PLATFORM'] == 'darwin': - #env.Append(CPPDEFINES = ['NDEBUG']) - env.Append(CCFLAGS = ['-Wall','-O0']) - #env.Append(CCFLAGS = ['-O3', '-fomit-frame-pointer']) + if env.has_key('DEBUG'): + env.Append(CCFLAGS = ['-g', '-O0']) + else: + env.Append(CPPDEFINES = ['NDEBUG']) + env.Append(CCFLAGS = ['-O3', '-fomit-frame-pointer']) + env.Append(CCFLAGS = ['-Wall']) #env['LINKFLAGS'] = '' if env['PLATFORM'].startswith('win'): diff --git a/examples/utility_client/utility_client.cpp b/examples/utility_client/utility_client.cpp index 19f45c27d3..6d22f7b8de 100644 --- a/examples/utility_client/utility_client.cpp +++ b/examples/utility_client/utility_client.cpp @@ -43,6 +43,10 @@ public: 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); diff --git a/test/transport/asio/SConscript b/test/transport/asio/SConscript index 909a2f320f..5377a89095 100644 --- a/test/transport/asio/SConscript +++ b/test/transport/asio/SConscript @@ -18,7 +18,7 @@ prgs = env.Program('test_base_boost', ["base_boost.o"], LIBS = BOOST_LIBS) #prgs += env.Program('test_utility_boost', ["utilities_boost.o"], LIBS = BOOST_LIBS) if env_cpp11.has_key('WSPP_CPP11_ENABLED'): - BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework'],env_cpp11) + [platform_libs] + [polyfill_libs] + BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework','system'],env_cpp11) + [platform_libs] + [polyfill_libs] objs += env_cpp11.Object('base_stl.o', ["base.cpp"], LIBS = BOOST_LIBS_CPP11) prgs += env_cpp11.Program('test_base_stl', ["base_stl.o"], LIBS = BOOST_LIBS_CPP11) diff --git a/test/transport/asio/base.cpp b/test/transport/asio/base.cpp index 33052cacd3..44a13019de 100644 --- a/test/transport/asio/base.cpp +++ b/test/transport/asio/base.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Peter Thorson. All rights reserved. + * Copyright (c) 2013, Peter Thorson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -33,17 +33,17 @@ #include BOOST_AUTO_TEST_CASE( blank_error ) { - websocketpp::lib::error_code ec; - + websocketpp::lib::error_code ec; + BOOST_CHECK( !ec ); } BOOST_AUTO_TEST_CASE( asio_error ) { - using websocketpp::transport::asio::error::make_error_code; - using websocketpp::transport::asio::error::general; - - websocketpp::lib::error_code ec = make_error_code(general); - + using websocketpp::transport::asio::error::make_error_code; + using websocketpp::transport::asio::error::general; + + websocketpp::lib::error_code ec = make_error_code(general); + BOOST_CHECK( ec == general ); BOOST_CHECK( ec.value() == 1 ); } \ No newline at end of file diff --git a/test/utility/frame.cpp b/test/utility/frame.cpp index f6adc5df19..d79637aa8a 100644 --- a/test/utility/frame.cpp +++ b/test/utility/frame.cpp @@ -407,7 +407,7 @@ BOOST_AUTO_TEST_CASE( continuous_word_mask ) { pkey_temp = frame::word_mask_circ(input+7,output+7,8,pkey_temp); BOOST_CHECK( std::equal(output,output+16,masked) ); - BOOST_CHECK( pkey_temp == frame::circshift_prepared_key(pkey,3) ); + BOOST_CHECK_EQUAL( pkey_temp, frame::circshift_prepared_key(pkey,3) ); } BOOST_AUTO_TEST_CASE( continuous_word_mask_inplace ) { @@ -437,11 +437,11 @@ BOOST_AUTO_TEST_CASE( continuous_word_mask_inplace ) { pkey_temp = frame::word_mask_circ(buffer,7,pkey); BOOST_CHECK( std::equal(buffer,buffer+7,masked) ); - BOOST_CHECK( pkey_temp == frame::circshift_prepared_key(pkey,3) ); + BOOST_CHECK_EQUAL( pkey_temp, frame::circshift_prepared_key(pkey,3) ); pkey_temp = frame::word_mask_circ(buffer+7,8,pkey_temp); BOOST_CHECK( std::equal(buffer,buffer+16,masked) ); - BOOST_CHECK( pkey_temp == frame::circshift_prepared_key(pkey,3) ); + BOOST_CHECK_EQUAL( pkey_temp, frame::circshift_prepared_key(pkey,3) ); } BOOST_AUTO_TEST_CASE( continuous_word_mask2 ) { diff --git a/websocketpp/config/asio_client.hpp b/websocketpp/config/asio_client.hpp index e169537a38..0c03142ecb 100644 --- a/websocketpp/config/asio_client.hpp +++ b/websocketpp/config/asio_client.hpp @@ -41,22 +41,23 @@ namespace config { struct asio_tls_client : public core_client { typedef asio_tls_client type; + typedef core_client base; - typedef core_client::concurrency_type concurrency_type; + typedef base::concurrency_type concurrency_type; - typedef core_client::request_type request_type; - typedef core_client::response_type response_type; + typedef base::request_type request_type; + typedef base::response_type response_type; - typedef core_client::message_type message_type; - typedef core_client::con_msg_manager_type con_msg_manager_type; - typedef core_client::endpoint_msg_manager_type endpoint_msg_manager_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 core_client::alog_type alog_type; - typedef core_client::elog_type elog_type; + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; - typedef core_client::rng_type rng_type; + typedef base::rng_type rng_type; - struct transport_config { + 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; diff --git a/websocketpp/config/asio_no_tls.hpp b/websocketpp/config/asio_no_tls.hpp index f4c1d9ea15..6b7bebe3bd 100644 --- a/websocketpp/config/asio_no_tls.hpp +++ b/websocketpp/config/asio_no_tls.hpp @@ -36,22 +36,23 @@ namespace config { struct asio : public core { typedef asio type; + typedef core base; - typedef core::concurrency_type concurrency_type; + typedef base::concurrency_type concurrency_type; - typedef core::request_type request_type; - typedef core::response_type response_type; + typedef base::request_type request_type; + typedef base::response_type response_type; - typedef core::message_type message_type; - typedef core::con_msg_manager_type con_msg_manager_type; - typedef core::endpoint_msg_manager_type endpoint_msg_manager_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 core::alog_type alog_type; - typedef core::elog_type elog_type; + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; - typedef core::rng_type rng_type; + typedef base::rng_type rng_type; - struct transport_config { + 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; diff --git a/websocketpp/config/asio_no_tls_client.hpp b/websocketpp/config/asio_no_tls_client.hpp index 7c6f1a87da..8f866ff43a 100644 --- a/websocketpp/config/asio_no_tls_client.hpp +++ b/websocketpp/config/asio_no_tls_client.hpp @@ -36,22 +36,23 @@ namespace config { struct asio_client : public core_client { typedef asio_client type; + typedef core_client base; - typedef core_client::concurrency_type concurrency_type; + typedef base::concurrency_type concurrency_type; - typedef core_client::request_type request_type; - typedef core_client::response_type response_type; + typedef base::request_type request_type; + typedef base::response_type response_type; - typedef core_client::message_type message_type; - typedef core_client::con_msg_manager_type con_msg_manager_type; - typedef core_client::endpoint_msg_manager_type endpoint_msg_manager_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 core_client::alog_type alog_type; - typedef core_client::elog_type elog_type; + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; - typedef core_client::rng_type rng_type; + typedef base::rng_type rng_type; - struct transport_config { + 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; diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp index eacee08f8a..6450e7d2f7 100644 --- a/websocketpp/config/core.hpp +++ b/websocketpp/config/core.hpp @@ -97,6 +97,17 @@ struct core { typedef type::alog_type alog_type; typedef type::request_type request_type; typedef type::response_type response_type; + + /// Default timer values (in ms) + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + /// Length of time to wait before a tls handshake is aborted + static const long timeout_tls_handshake = 5000; + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; }; /// Transport Endpoint Component @@ -108,6 +119,15 @@ struct core { /// User overridable Connection base class typedef websocketpp::connection_base connection_base; + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + /// WebSocket Protocol version to use as a client /** * What version of the WebSocket Protocol to use for outgoing client diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp index 653167bb02..0d032502fb 100644 --- a/websocketpp/config/core_client.hpp +++ b/websocketpp/config/core_client.hpp @@ -96,6 +96,17 @@ struct core_client { typedef type::alog_type alog_type; typedef type::request_type request_type; typedef type::response_type response_type; + + /// Default timer values (in ms) + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + /// Length of time to wait before a tls handshake is aborted + static const long timeout_tls_handshake = 5000; + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; }; /// Transport Endpoint Component @@ -107,6 +118,15 @@ struct core_client { /// User overridable Connection base class typedef websocketpp::connection_base connection_base; + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + /// WebSocket Protocol version to use as a client /** * What version of the WebSocket Protocol to use for outgoing client diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp index 51b2ecbd85..eb3a11e7dc 100644 --- a/websocketpp/config/debug.hpp +++ b/websocketpp/config/debug.hpp @@ -97,6 +97,17 @@ struct debug_core { typedef type::alog_type alog_type; typedef type::request_type request_type; typedef type::response_type response_type; + + /// Default timer values (in ms) + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + /// Length of time to wait before a tls handshake is aborted + static const long timeout_tls_handshake = 5000; + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; }; /// Transport Endpoint Component @@ -108,6 +119,15 @@ struct debug_core { /// User overridable Connection base class typedef websocketpp::connection_base connection_base; + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + /// WebSocket Protocol version to use as a client /** * What version of the WebSocket Protocol to use for outgoing client diff --git a/websocketpp/config/debug_asio.hpp b/websocketpp/config/debug_asio.hpp index 37f6c98503..1545f10a7e 100644 --- a/websocketpp/config/debug_asio.hpp +++ b/websocketpp/config/debug_asio.hpp @@ -57,7 +57,7 @@ struct debug_asio_tls : public debug_core { typedef base::rng_type rng_type; - struct transport_config { + 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; diff --git a/websocketpp/config/debug_asio_no_tls.hpp b/websocketpp/config/debug_asio_no_tls.hpp index 6942190153..81c7f40d5b 100644 --- a/websocketpp/config/debug_asio_no_tls.hpp +++ b/websocketpp/config/debug_asio_no_tls.hpp @@ -52,7 +52,7 @@ struct debug_asio : public debug_core { typedef base::rng_type rng_type; - struct transport_config { + 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; diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index e54209de40..55150b2b64 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -167,6 +167,14 @@ public: // Misc Convenience Types typedef session::internal_state::value istate_type; +private: + enum terminate_status { + failed = 1, + closed, + unknown + }; +public: + explicit connection(bool is_server, const std::string& ua, alog_type& alog, elog_type& elog, rng_type & rng) : transport_con_type(is_server,alog,elog) @@ -773,14 +781,12 @@ public: void start(); - void read(size_t num_bytes); - void handle_read(const lib::error_code& ec, size_t bytes_transferred); - + void read_handshake(size_t num_bytes); - void write(std::string msg); - void handle_write(const lib::error_code& ec); + //void write(std::string msg); + //void handle_write(const lib::error_code& ec); - void handle_handshake_read(const lib::error_code& ec, + void handle_read_handshake(const lib::error_code& ec, size_t bytes_transferred); void handle_read_http_response(const lib::error_code& ec, size_t bytes_transferred); @@ -798,7 +804,8 @@ public: /// internally by the endpoint class. void set_termination_handler(termination_handler new_handler); - void terminate(); + void terminate(const lib::error_code & ec); + void handle_terminate(terminate_status tstat, const lib::error_code& ec); /// Checks if there are frames in the send queue and if there are sends one /** diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp index e094b867f2..c7e49d901f 100644 --- a/websocketpp/error.hpp +++ b/websocketpp/error.hpp @@ -100,6 +100,9 @@ enum value { /// Attempted to use a server specific feature on a client endpoint server_only, + + /// HTTP connection ended + http_connection_ended }; // enum value @@ -153,6 +156,8 @@ public: return "Feature not available on server endpoints"; case error::server_only: return "Feature not available on client endpoints"; + case error::http_connection_ended: + return "HTTP connection ended"; default: return "Unknown"; } diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 1b0b0f02d4..9919afccdb 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -552,30 +552,23 @@ void connection::handle_transport_init(const lib::error_code& ec) { s << "handle_transport_init recieved error: "<< ec.message(); m_elog.write(log::elevel::fatal,s.str()); - this->terminate(); + this->terminate(ec); return; } // At this point the transport is ready to read and write bytes. - if (m_is_server) { - this->read(1); + this->read_handshake(1); } else { // We are a client. Set the processor to the version specified in the // config file and send a handshake request. m_processor = get_processor(config::client_version); this->send_http_request(); } - - // TODO: Begin websocket handshake - // server: read/process/write/go - // client: process/write/read/process/go - - //this->read(); } template -void connection::read(size_t num_bytes) { +void connection::read_handshake(size_t num_bytes) { m_alog.write(log::alevel::devel,"connection read"); transport_con_type::async_read_at_least( @@ -583,7 +576,7 @@ void connection::read(size_t num_bytes) { m_buf, config::connection_read_buffer_size, lib::bind( - &type::handle_handshake_read, + &type::handle_read_handshake, type::shared_from_this(), lib::placeholders::_1, lib::placeholders::_2 @@ -594,28 +587,28 @@ void connection::read(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 -void connection::handle_handshake_read(const lib::error_code& ec, +void connection::handle_read_handshake(const lib::error_code& ec, size_t bytes_transferred) { - m_alog.write(log::alevel::devel,"connection handle_handshake_read"); + m_alog.write(log::alevel::devel,"connection handle_read_handshake"); this->atomic_state_check( istate::READ_HTTP_REQUEST, - "handle_handshake_read must be called from READ_HTTP_REQUEST state" + "handle_read_handshake must be called from READ_HTTP_REQUEST state" ); if (ec) { std::stringstream s; s << "error in handle_read_handshake: "<< ec.message(); m_elog.write(log::elevel::fatal,s.str()); - this->terminate(); + this->terminate(ec); return; } // Boundaries checking. TODO: How much of this should be done? if (bytes_transferred > config::connection_read_buffer_size) { m_elog.write(log::elevel::fatal,"Fatal boundaries checking error."); - this->terminate(); + this->terminate(make_error_code(error::general)); return; } @@ -634,7 +627,7 @@ void connection::handle_handshake_read(const lib::error_code& ec, // TODO: Is this overkill? if (bytes_processed > config::connection_read_buffer_size) { m_elog.write(log::elevel::fatal,"Fatal boundaries checking error."); - this->terminate(); + this->terminate(make_error_code(error::general)); return; } @@ -699,7 +692,7 @@ void connection::handle_handshake_read(const lib::error_code& ec, m_buf, config::connection_read_buffer_size, lib::bind( - &type::handle_handshake_read, + &type::handle_read_handshake, type::shared_from_this(), lib::placeholders::_1, lib::placeholders::_2 @@ -745,21 +738,22 @@ void connection::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, ignore for the moment"); + m_elog.write(log::elevel::rerror,"got TLS short read, killing connection for now"); + this->terminate(ec); return; } std::stringstream s; s << "error in handle_read_frame: " << ec.message() << " (" << ec << ")"; m_elog.write(log::elevel::fatal,s.str()); - this->terminate(); + this->terminate(ec); return; } // Boundaries checking. TODO: How much of this should be done? if (bytes_transferred > config::connection_read_buffer_size) { m_elog.write(log::elevel::fatal,"Fatal boundaries checking error"); - this->terminate(); + this->terminate(make_error_code(error::general)); return; } @@ -795,7 +789,7 @@ void connection::handle_read_frame(const lib::error_code& ec, m_elog.write(log::elevel::rerror,"consume error: "+ec.message()); if (config::drop_on_protocol_error) { - this->terminate(); + this->terminate(ec); return; } else { lib::error_code close_ec; @@ -805,7 +799,7 @@ void connection::handle_read_frame(const lib::error_code& ec, m_elog.write(log::elevel::fatal, "Failed to send a close frame after protocol error: " +close_ec.message()); - this->terminate(); + this->terminate(close_ec); return; } } @@ -992,62 +986,7 @@ bool connection::process_handshake_request() { } return true; -} - -// TODO: does this function still need to be here? -template -void connection::handle_read(const lib::error_code& ec, - size_t bytes_transferred) -{ - if (ec) { - m_elog.write(log::elevel::rerror,"error in handle_read"+ec.message()); - return; - } - - // TODO: assert bytes_transferred < m_buf size. - - m_alog.write(log::alevel::devel,"connection handle_read"); - - std::string foo(m_buf,bytes_transferred); - - // process bytes - - if (foo == "close") { - this->terminate(); - return; - } - - //m_handler->on_message(type::shared_from_this(),foo); - - this->read(); -} - - -template -void connection::write(std::string msg) { - m_alog.write(log::alevel::devel,"connection write"); - - transport_con_type::async_write( - msg.data(), - msg.size(), - lib::bind( - &type::handle_write, - type::shared_from_this(), - lib::placeholders::_1 - ) - ); -} - -template -void connection::handle_write(const lib::error_code& ec) { - if (ec) { - m_elog.write(log::elevel::rerror, - "error in handle_write: "+ec.message()); - return; - } - - m_alog.write(log::alevel::devel,"connection handle_write"); -} +} template void connection::send_http_response() { @@ -1106,7 +1045,7 @@ void connection::handle_send_http_response( if (ec) { m_elog.write(log::elevel::rerror, "error in handle_send_http_response: "+ec.message()); - this->terminate(); + this->terminate(ec); return; } @@ -1124,7 +1063,7 @@ void connection::handle_send_http_response( << m_response.get_status_code(); m_elog.write(log::elevel::rerror,s.str()); } - this->terminate(); + this->terminate(make_error_code(error::http_connection_ended)); return; } @@ -1204,7 +1143,7 @@ void connection::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()); - this->terminate(); + this->terminate(ec); return; } @@ -1243,16 +1182,17 @@ void connection::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()); - this->terminate(); + this->terminate(ec); return; } size_t bytes_processed = 0; + // TODO: refactor this to use error codes rather than exceptions try { bytes_processed = m_response.consume(m_buf,bytes_transferred); } catch (http::exception & e) { m_elog.write(log::elevel::rerror, std::string("error in handle_read_http_response: ")+e.what()); - this->terminate(); + this->terminate(make_error_code(error::general)); return; } @@ -1268,7 +1208,7 @@ void connection::handle_read_http_response(const lib::error_code& ec, std::string("Server handshake response was invalid: ")+ ec.message() ); - this->terminate(); + this->terminate(ec); return; } @@ -1312,32 +1252,68 @@ void connection::handle_read_http_response(const lib::error_code& ec, } template -void connection::terminate() { - try { - m_alog.write(log::alevel::devel,"connection terminate"); - - transport_con_type::shutdown(); - - if (m_state == session::state::connecting) { - m_state = session::state::closed; - if (m_fail_handler) { - m_fail_handler(m_connection_hdl); - } - } else if (m_state != session::state::closed) { - m_state = session::state::closed; - if (m_close_handler) { - m_close_handler(m_connection_hdl); - } - } else { - m_alog.write(log::alevel::devel,"terminate called on connection that was already terminated"); - return; - } - - log_close_result(); - } catch (const std::exception& e) { - m_elog.write(log::elevel::warn, - std::string("terminate failed. Reason was: ") + e.what()); +void connection::terminate(const lib::error_code & ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection "); } + + terminate_status tstat = unknown; + if (ec) { + m_local_close_code = close::status::abnormal_close; + m_local_close_reason = ec.message(); + } + + if (m_state == session::state::connecting) { + m_state = session::state::closed; + tstat = failed; + } else if (m_state != session::state::closed) { + m_state = session::state::closed; + tstat = closed; + } else { + m_alog.write(log::alevel::devel, + "terminate called on connection that was already terminated"); + return; + } + + transport_con_type::async_shutdown( + lib::bind( + &type::handle_terminate, + type::shared_from_this(), + tstat, + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_terminate(terminate_status tstat, + const lib::error_code& ec) +{ + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection handle_terminate"); + } + + if (ec) { + // there was an error actually shutting down the connection + m_elog.write(log::elevel::rerror,ec.message()); + } + + // clean shutdown + if (tstat == failed) { + if (m_fail_handler) { + m_fail_handler(m_connection_hdl); + } + // TODO: custom fail output log format? + log_close_result(); + } else if (tstat == closed) { + if (m_close_handler) { + m_close_handler(m_connection_hdl); + } + log_close_result(); + } else { + m_elog.write(log::elevel::rerror,"Unknown terminate_status"); + } + // call the termination handler if it exists // if it exists it might (but shouldn't) refer to a bad memory location. // If it does, we don't care and should catch and ignore it. @@ -1419,19 +1395,21 @@ template void connection::handle_write_frame(bool terminate, const lib::error_code& ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection handle_write_frame"); + } + m_send_buffer.clear(); m_current_msg.reset(); if (ec) { m_elog.write(log::elevel::fatal,"error in handle_write_frame: "+ec.message()); - this->terminate(); + this->terminate(ec); return; } - m_alog.write(log::alevel::devel,"connection handle_write_frame"); - if (terminate) { - this->terminate(); + this->terminate(lib::error_code()); return; } @@ -1544,7 +1522,7 @@ void connection::process_control_frame(typename s << "Received invalid close code " << m_remote_close_code << " dropping connection per config."; m_elog.write(log::elevel::devel,s.str()); - this->terminate(); + this->terminate(ec); } else { s << "Received invalid close code " << m_remote_close_code << " sending acknowledgement and closing"; @@ -1564,7 +1542,7 @@ void connection::process_control_frame(typename if (config::drop_on_protocol_error) { m_elog.write(log::elevel::devel, "Received invalid close reason. Dropping connection per config"); - this->terminate(); + this->terminate(ec); } else { m_elog.write(log::elevel::devel, "Received invalid close reason. Sending acknowledgement and closing"); @@ -1592,7 +1570,7 @@ void connection::process_control_frame(typename } else if (m_state == session::state::closing) { // ack of our close m_alog.write(log::alevel::devel,"Got acknowledgement of close"); - this->terminate(); + this->terminate(lib::error_code()); } else { // spurious, ignore m_elog.write(log::elevel::devel,"Got close frame in wrong state"); diff --git a/websocketpp/roles/client_endpoint.hpp b/websocketpp/roles/client_endpoint.hpp index 90539682e8..e07ed57de6 100644 --- a/websocketpp/roles/client_endpoint.hpp +++ b/websocketpp/roles/client_endpoint.hpp @@ -155,7 +155,7 @@ private: } else if (ec) { // TODO // Set connection's failure reasons - con->terminate(); + con->terminate(ec); endpoint_type::m_elog.write(log::elevel::rerror, "handle_connect error: "+ec.message()); diff --git a/websocketpp/roles/server_endpoint.hpp b/websocketpp/roles/server_endpoint.hpp index 723417e7ba..aa081ae109 100644 --- a/websocketpp/roles/server_endpoint.hpp +++ b/websocketpp/roles/server_endpoint.hpp @@ -114,7 +114,7 @@ public: //con->terminate(); } else { if (ec) { - con->terminate(); + con->terminate(ec); endpoint_type::m_elog.write(log::elevel::rerror, "handle_accept error: "+ec.message()); diff --git a/websocketpp/transport/asio/base.hpp b/websocketpp/transport/asio/base.hpp index 3796fae2f0..f313b916e4 100644 --- a/websocketpp/transport/asio/base.hpp +++ b/websocketpp/transport/asio/base.hpp @@ -28,8 +28,11 @@ #ifndef WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP #define WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP -#include #include +#include +#include + +#include #include @@ -37,6 +40,9 @@ namespace websocketpp { namespace transport { namespace asio { +typedef lib::function + socket_shutdown_handler; + /** * This policy uses a single boost::asio io_service to provide transport * services to a WebSocket++ endpoint. diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp index 7cf155bf28..3ae90f886d 100644 --- a/websocketpp/transport/asio/connection.hpp +++ b/websocketpp/transport/asio/connection.hpp @@ -80,7 +80,9 @@ public: /// Type of a pointer to the ASIO io_service being used typedef boost::asio::io_service* io_service_ptr; - + /// Type of a pointer to the ASIO timer class + typedef lib::shared_ptr timer_ptr; + // generate and manage our own io_service explicit connection(bool is_server, alog_type& alog, elog_type& elog) : m_is_server(is_server) @@ -121,18 +123,94 @@ public: m_tcp_init_handler = h; } - void set_proxy(const std::string & proxy) { - m_proxy = proxy; + /// Set the proxy to connect through (exception free) + /** + * The URI passed should be a complete URI including scheme. For example: + * http://proxy.example.com:8080/ + * + * The proxy must be set up as an explicit (CONNECT) proxy allowed to + * connect to the port you specify. Traffic to the proxy is not encrypted. + * + * @param uri The full URI of the proxy to connect to. + * + * @param ec A status value + */ + void set_proxy(const std::string & uri, lib::error_code & ec) { + // TODO: return errors for illegal URIs here? + // TODO: should https urls be illegal for the moment? + m_proxy = uri; m_proxy_data.reset(new proxy_data()); + ec = lib::error_code(); } - void set_proxy_basic_auth(const std::string & u, const std::string & p) { - if (m_proxy_data) { - std::string val = "Basic "+base64_encode(u + ":" + p); - m_proxy_data->req.replace_header("Proxy-Authorization",val); - } else { - // TODO: should we throw errors with invalid stuff here or just - // silently ignore? + + /// Set the proxy to connect through (exception) + void set_proxy(const std::string & uri) { + lib::error_code ec; + set_proxy(uri,ec); + if (ec) { throw ec; } + } + + /// Set the basic auth credentials to use (exception free) + /** + * The URI passed should be a complete URI including scheme. For example: + * http://proxy.example.com:8080/ + * + * The proxy must be set up as an explicit proxy + * + * @param username The username to send + * + * @param password The password to send + * + * @param ec A status value + */ + void set_proxy_basic_auth(const std::string & username, const + std::string & password, lib::error_code & ec) + { + if (!m_proxy_data) { + ec = make_error_code(websocketpp::error::invalid_state); + return; } + + // TODO: username can't contain ':' + std::string val = "Basic "+base64_encode(username + ":" + password); + m_proxy_data->req.replace_header("Proxy-Authorization",val); + ec = lib::error_code(); + } + + /// Set the basic auth credentials to use (exception) + void set_proxy_basic_auth(const std::string & username, const + std::string & password) + { + lib::error_code ec; + set_proxy_basic_auth(username,password,ec); + if (ec) { throw ec; } + } + + /// Set the proxy timeout duration (exception free) + /** + * Duration is in milliseconds. Default value is based on the transport + * config + * + * @param duration The number of milliseconds to wait before aborting the + * proxy connection. + * + * @param ec A status value + */ + void set_proxy_timeout(long duration, lib::error_code & ec) { + if (!m_proxy_data) { + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_proxy_data->timeout_proxy = duration; + ec = lib::error_code(); + } + + /// Set the proxy timeout duration (exception) + void set_proxy_timeout(long duration) { + lib::error_code ec; + set_proxy_timeout(duration,ec); + if (ec) { throw ec; } } const std::string & get_proxy() const { @@ -175,7 +253,8 @@ public: */ lib::error_code proxy_init(const std::string & authority) { if (!m_proxy_data) { - return websocketpp::error::make_error_code(websocketpp::error::invalid_state); + return websocketpp::error::make_error_code( + websocketpp::error::invalid_state); } m_proxy_data->req.set_version("HTTP/1.1"); m_proxy_data->req.set_method("CONNECT"); @@ -185,7 +264,67 @@ public: return lib::error_code(); } + + /// Call back a function after a period of time. + /** + * Sets a timer that calls back a function after the specified period of + * milliseconds. Returns a handle that can be used to cancel the timer. + * A cancelled timer will return the error code error::operation_aborted + * A timer that expired will return no error. + * + * @param duration Length of time to wait in milliseconds + * + * @param callback The function to call back when the timer has expired + * + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler callback) { + timer_ptr new_timer( + new boost::asio::deadline_timer( + *m_io_service, + boost::posix_time::milliseconds(duration) + ) + ); + new_timer->async_wait( + lib::bind( + &type::handle_timer, + this, + new_timer, + callback, + lib::placeholders::_1 + ) + ); + + return new_timer; + } + + /// Timer callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param t Pointer to the timer in question + * + * @param callback The function to call back + * + * @param ec The status code + */ + void handle_timer(timer_ptr t, timer_handler callback, const + boost::system::error_code& ec) + { + if (ec) { + if (ec == boost::asio::error::operation_aborted) { + callback(make_error_code(transport::error::operation_aborted)); + } else { + log_err(log::elevel::info,"asio handle_timer",ec); + callback(make_error_code(error::pass_through)); + } + } else { + callback(lib::error_code()); + } + } protected: /// Initialize transport for reading /** @@ -232,7 +371,8 @@ protected: callback(ec); } - // If we have a proxy set issue a proxy connect, otherwise skip to post_init + // If we have a proxy set issue a proxy connect, otherwise skip to + // post_init if (!m_proxy.empty()) { proxy_write(callback); } else { @@ -281,7 +421,19 @@ protected: m_proxy_data->write_buf.size())); m_alog.write(log::alevel::devel,m_proxy_data->write_buf); - + + // Set a timer so we don't wait forever for the proxy to respond + m_proxy_data->timer = this->set_timer( + m_proxy_data->timeout_proxy, + lib::bind( + &type::handle_proxy_timeout, + this, + callback, + lib::placeholders::_1 + ) + ); + + // Send proxy request boost::asio::async_write( socket_con_type::get_next_layer(), m_bufs, @@ -294,6 +446,21 @@ protected: ); } + void handle_proxy_timeout(init_handler callback, const lib::error_code & ec) { + if (ec == transport::error::operation_aborted) { + m_alog.write(log::alevel::devel, + "asio handle_proxy_write timer cancelled"); + return; + } else if (ec) { + log_err(log::elevel::devel,"asio handle_proxy_write",ec); + callback(ec); + } else { + m_alog.write(log::alevel::devel, + "asio handle_proxy_write timer expired"); + callback(make_error_code(transport::error::timeout)); + } + } + void handle_proxy_write(init_handler callback, const boost::system::error_code& ec) { @@ -304,8 +471,8 @@ protected: m_bufs.clear(); if (ec) { - m_elog.write(log::elevel::info, - "asio handle_proxy_write error: "+ec.message()); + log_err(log::elevel::info,"asio handle_proxy_write",ec); + m_proxy_data->timer->cancel(); callback(make_error_code(error::pass_through)); } else { proxy_read(callback); @@ -320,6 +487,7 @@ protected: if (!m_proxy_data) { m_elog.write(log::elevel::library, "assertion failed: !m_proxy_data in asio::connection::proxy_read"); + m_proxy_data->timer->cancel(); callback(make_error_code(error::general)); return; } @@ -345,6 +513,9 @@ protected: m_alog.write(log::alevel::devel,"asio connection handle_proxy_read"); } + // At this point there is no need to wait for the timer anymore + m_proxy_data->timer->cancel(); + if (ec) { m_elog.write(log::elevel::info, "asio handle_proxy_read error: "+ec.message()); @@ -453,13 +624,7 @@ protected: handler(make_error_code(transport::error::tls_short_read), bytes_transferred); } else { - // other error that we cannot translate into a WebSocket++ - // transport error. Use pass through and print an info warning - // with the original error. - std::stringstream s; - s << "asio async_read_at_least error: " - << ec << " (" << ec.message() << ")"; - m_elog.write(log::elevel::info,s.str()); + log_err(log::elevel::info,"asio async_read_at_least",ec); handler(make_error_code(transport::error::pass_through), bytes_transferred); } @@ -503,9 +668,9 @@ protected: boost::system::error_code& ec) { m_bufs.clear(); - // TODO: translate this better if (ec) { - handler(make_error_code(error::pass_through)); + log_err(log::elevel::info,"asio async_write",ec); + handler(make_error_code(transport::error::pass_through)); } else { handler(lib::error_code()); } @@ -544,44 +709,58 @@ protected: }*/ /// close and clean up the underlying socket - void shutdown() { - socket_con_type::shutdown(); + void async_shutdown(shutdown_handler h) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection async_shutdown"); + } + + socket_con_type::async_shutdown( + lib::bind( + &type::handle_async_shutdown, + this, + h, + lib::placeholders::_1 + ) + ); } - typedef lib::shared_ptr timer_ptr; - - timer_ptr set_timer(long duration, timer_handler handler) { - timer_ptr timer(new boost::asio::deadline_timer(*m_io_service)); - timer->expires_from_now(boost::posix_time::milliseconds(duration)); - timer->async_wait(lib::bind(&type::timer_handler, this, handler, - lib::placeholders::_1)); - return timer; - } - - void timer_handler(timer_handler h, const boost::system::error_code& ec) { - if (ec == boost::asio::error::operation_aborted) { - h(make_error_code(transport::error::operation_aborted)); - } else if (ec) { - std::stringstream s; - s << "asio async_wait error::pass_through" - << "Original Error: " << ec << " (" << ec.message() << ")"; - m_elog.write(log::elevel::devel,s.str()); + void handle_async_shutdown(shutdown_handler h, const + boost::system::error_code & ec) + { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio con handle_async_shutdown"); + } + + if (ec) { + log_err(log::elevel::info,"asio async_shutdown",ec); h(make_error_code(transport::error::pass_through)); } else { h(lib::error_code()); } } private: + /// Convenience method for logging the code and message for an error_code + std::string log_err(log::level l,const char * msg, lib::error_code & ec) + { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog->write(l,s.str()); + } + // static settings - const bool m_is_server; + const bool m_is_server; alog_type& m_alog; elog_type& m_elog; struct proxy_data { + proxy_data() : timeout_proxy(config::timeout_proxy) {} + request_type req; response_type res; std::string write_buf; boost::asio::streambuf read_buf; + long timeout_proxy; + timer_ptr timer; }; std::string m_proxy; diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp index 709754580f..8a42311f84 100644 --- a/websocketpp/transport/asio/endpoint.hpp +++ b/websocketpp/transport/asio/endpoint.hpp @@ -74,14 +74,16 @@ public: /// Type of a shared pointer to the connection transport component /// associated with this endpoint transport component typedef typename transport_con_type::ptr transport_con_ptr; - + /// Type of a pointer to the ASIO io_service being used typedef boost::asio::io_service* io_service_ptr; /// Type of a shared pointer to the acceptor being used typedef lib::shared_ptr acceptor_ptr; /// Type of a shared pointer to the resolver being used typedef lib::shared_ptr resolver_ptr; - + /// Type of timer handle + typedef lib::shared_ptr timer_ptr; + // generate and manage our own io_service explicit endpoint() : m_external_io_service(false) @@ -293,28 +295,68 @@ public: listen(*endpoint_iterator); } - typedef lib::shared_ptr timer_ptr; - - timer_ptr set_timer(long duration, timer_handler handler) { - timer_ptr timer(new boost::asio::deadline_timer(*m_io_service)); - timer->expires_from_now(boost::posix_time::milliseconds(duration)); - timer->async_wait(lib::bind(&type::timer_handler, this, handler, - lib::placeholders::_1)); - return timer; - } - - void timer_handler(timer_handler h, const boost::system::error_code& ec) { - if (ec == boost::asio::error::operation_aborted) { - h(make_error_code(transport::error::operation_aborted)); - } else if (ec) { - std::stringstream s; - s << "asio async_wait error: " << ec << " (" << ec.message() << ")"; - m_elog->write(log::elevel::devel,s.str()); - h(make_error_code(transport::error::pass_through)); + /// Call back a function after a period of time. + /** + * Sets a timer that calls back a function after the specified period of + * milliseconds. Returns a handle that can be used to cancel the timer. + * A cancelled timer will return the error code error::operation_aborted + * A timer that expired will return no error. + * + * @param duration Length of time to wait in milliseconds + * + * @param callback The function to call back when the timer has expired + * + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler callback) { + timer_ptr new_timer( + new boost::asio::deadline_timer( + *m_io_service, + boost::posix_time::milliseconds(duration) + ) + ); + + new_timer->async_wait( + lib::bind( + &type::handle_timer, + this, + new_timer, + callback, + lib::placeholders::_1 + ) + ); + + return new_timer; + } + + /// Timer callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param t Pointer to the timer in question + * + * @param callback The function to call back + * + * @param ec The status code + */ + void handle_timer(timer_ptr t, timer_handler callback, const + boost::system::error_code& ec) + { + if (ec) { + if (ec == boost::asio::error::operation_aborted) { + callback(make_error_code(transport::error::operation_aborted)); + } else { + m_elog->write(log::elevel::info, + "asio handle_timer error: "+ec.message()); + log_err(log::elevel::info,"asio handle_timer",ec); + callback(make_error_code(error::pass_through)); + } } else { - h(lib::error_code()); + callback(lib::error_code()); } - } + } boost::asio::io_service& get_io_service() { return *m_io_service; @@ -396,12 +438,25 @@ protected: "starting async DNS resolve for "+host+":"+port); } + timer_ptr dns_timer = tcon->set_timer( + config::timeout_dns, + lib::bind( + &type::handle_resolve_timeout, + this, + tcon, + dns_timer, + cb, + lib::placeholders::_1 + ) + ); + m_resolver->async_resolve( query, lib::bind( &type::handle_resolve, this, tcon, + dns_timer, cb, lib::placeholders::_1, lib::placeholders::_2 @@ -409,28 +464,48 @@ protected: ); } + void handle_resolve_timeout(transport_con_ptr tcon, timer_ptr dns_timer, + connect_handler callback, const lib::error_code & ec) + { + m_resolver->cancel(); + + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio handle_resolve_timeout timer cancelled"); + } else if (ec) { + log_err(log::elevel::devel,"asio handle_resolve_timeout",ec); + callback(tcon->get_handle(),ec); + } else { + m_alog->write(log::alevel::devel, + "asio handle_resolve_timeout timer expired"); + callback(tcon->get_handle(), + make_error_code(transport::error::timeout)); + } + } + void handle_resolve(transport_con_ptr tcon, connect_handler callback, - const boost::system::error_code& ec, + timer_ptr dns_timer, const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator iterator) { + dns_timer->cancel(); + if (ec) { - //con->terminate(); - // TODO: Better translation of errors at this point - std::stringstream s; - s << "asio async_resolve error:" - << ec << " (" << ec.message() << ")"; - m_elog->write(log::elevel::info,s.str()); + if (ec == boost::asio::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio handle_resolve resolve cancelled"); + return; + } + + log_err(log::elevel::info,"asio async_resolve",ec); callback(tcon->get_handle(),make_error_code(error::pass_through)); return; } if (m_alog->static_test(log::alevel::devel)) { std::stringstream s; - s << "Async DNS resolve successful. Results: "; boost::asio::ip::tcp::resolver::iterator it, end; - for (it = iterator; it != end; ++it) { s << (*it).endpoint() << " "; } @@ -456,12 +531,7 @@ protected: const boost::system::error_code& ec) { if (ec) { - //con->terminate(); - // TODO: Better translation of errors at this point - std::stringstream s; - s << "asio async_connect error: " - << ec << " (" << ec.message() << ")"; - m_elog->write(log::elevel::info,s.str()); + log_err(log::elevel::info,"asio async_connect",ec); callback(tcon->get_handle(),make_error_code(error::pass_through)); return; } @@ -506,6 +576,14 @@ protected: return lib::error_code(); } private: + /// Convenience method for logging the code and message for an error_code + std::string log_err(log::level l,const char * msg, lib::error_code & ec) + { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog->write(l,s.str()); + } + enum state { UNINITIALIZED = 0, READY = 1, diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp index 373bdfb89c..68e77e4451 100644 --- a/websocketpp/transport/asio/security/none.hpp +++ b/websocketpp/transport/asio/security/none.hpp @@ -205,11 +205,10 @@ protected: m_hdl = hdl; } - void shutdown() { + void async_shutdown(socket_shutdown_handler h) { boost::system::error_code ec; m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both,ec); - - // TODO: handle errors + h(ec); } private: enum state { diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index 946c49c2de..682eb5e66b 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -291,20 +291,23 @@ protected: callback(lib::error_code()); } - void handle_shutdown(socket_ptr s, const boost::system::error_code& ec) { - // TODO: error handling? - } - - void shutdown() { + void async_shutdown(socket_shutdown_handler h) { m_socket->async_shutdown( - lib::bind( - &type::handle_shutdown, + lib::bind( + &type::handle_async_shutdown, this, m_socket, + h, lib::placeholders::_1 ) ); } + + void handle_async_shutdown(socket_ptr s, socket_shutdown_handler h, const + boost::system::error_code& ec) + { + h(ec); + } private: socket_type::handshake_type get_handshake_type() { if (m_is_server) { diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp index d3ccaafb69..89464312dd 100644 --- a/websocketpp/transport/base/connection.hpp +++ b/websocketpp/transport/base/connection.hpp @@ -70,6 +70,7 @@ typedef lib::function init_handler; typedef lib::function read_handler; typedef lib::function write_handler; typedef lib::function timer_handler; +typedef lib::function shutdown_handler; typedef lib::function inturrupt_handler; typedef lib::function dispatch_handler; @@ -107,7 +108,10 @@ enum value { eof, /// TLS short read - tls_short_read + tls_short_read, + + /// Timer expired + timeout }; class category : public lib::error_category { @@ -134,6 +138,8 @@ class category : public lib::error_category { return "End of File"; case tls_short_read: return "TLS Short Read"; + case timeout: + return "Timer Expired"; default: return "Unknown"; } diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp index ed801e421c..ab2b19b01d 100644 --- a/websocketpp/transport/iostream/connection.hpp +++ b/websocketpp/transport/iostream/connection.hpp @@ -313,8 +313,8 @@ protected: return lib::error_code(); } - void shutdown() { - // TODO: + void async_shutdown(shutdown_handler h) { + h(lib::error_code()); } private: void read(std::istream &in) {