Skip to content

Commit

Permalink
Misc changes related to translation of asio transport errors #448
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Thorson committed Aug 20, 2015
1 parent f9dbc0f commit fb829e0
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 26 deletions.
23 changes: 16 additions & 7 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
HEAD
- Feature: Basic support for the permessage-deflate extension. #344
- Improvement: Better automatic std::chrono feature detection for Visual Studio
- Improvement: Major refactoring to bundled CMake build system. CMake can now be used to
build all of the examples and the test suite. Thank you Thijs Wenker for a significant
portion of this code. #378, #435, #449
- 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 Fidler for
reporting, test cases, and a patch. #456
- Improvement: Major refactoring to bundled CMake build system. CMake can now be
used to build all of the examples and the test suite. Thank you Thijs Wenker
for a significant portion of this code. #378, #435, #449
- Improvement: In build environments where `lib::error_code` and
`lib::asio::error_code` match (such as using `boost::asio` with
`boost::system_error` or standalone asio with `std::system_error`, transport
errors are passed through natively rather than being reported as a translated
`pass_through` error type.
- Improvement: Add a `get_transport_error` method to Asio transport connections
to allow retrieving a machine readable native transport error.
- 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
Fidler for reporting, test cases, and a patch. #456
- Bug: Fix an issue where standalone Asio builds that use TLS would not compile
due to lingering boost code. #448 Thank you mjsp for reporting

0.6.0
- MINOR BREAKING TRANSPORT POLICY CHANGE: Custom transport policies will now be
Expand Down
10 changes: 10 additions & 0 deletions test/transport/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ link_openssl()
final_target ()
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")

# Test transport asio security
file (GLOB SOURCE asio/security.cpp)

init_target (test_transport_asio_security)
build_executable (${TARGET_NAME} ${SOURCE})
link_boost ()
link_openssl()
final_target ()
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")

endif()

# Test transport iostream base
Expand Down
4 changes: 4 additions & 0 deletions test/transport/asio/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@ BOOST_LIBS = boostlibs(['unit_test_framework','system','thread','chrono'],env) +

objs = env.Object('base_boost.o', ["base.cpp"], LIBS = BOOST_LIBS)
objs += env.Object('timers_boost.o', ["timers.cpp"], LIBS = BOOST_LIBS)
objs += env.Object('security_boost.o', ["security.cpp"], LIBS = BOOST_LIBS)
prgs = env.Program('test_base_boost', ["base_boost.o"], LIBS = BOOST_LIBS)
prgs += env.Program('test_timers_boost', ["timers_boost.o"], LIBS = BOOST_LIBS)
prgs += env.Program('test_security_boost', ["security_boost.o"], LIBS = BOOST_LIBS)

if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework','system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
objs += env_cpp11.Object('base_stl.o', ["base.cpp"], LIBS = BOOST_LIBS_CPP11)
objs += env_cpp11.Object('timers_stl.o', ["timers.cpp"], LIBS = BOOST_LIBS_CPP11)
objs += env_cpp11.Object('security_stl.o', ["security.cpp"], LIBS = BOOST_LIBS_CPP11)
prgs += env_cpp11.Program('test_base_stl', ["base_stl.o"], LIBS = BOOST_LIBS_CPP11)
prgs += env_cpp11.Program('test_timers_stl', ["timers_stl.o"], LIBS = BOOST_LIBS_CPP11)
prgs += env_cpp11.Program('test_security_stl', ["security_stl.o"], LIBS = BOOST_LIBS_CPP11)

Return('prgs')
69 changes: 69 additions & 0 deletions test/transport/asio/security.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.
*
*/
//#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE transport_asio_base
#include <boost/test/unit_test.hpp>

#include <iostream>

#include <websocketpp/common/type_traits.hpp>

#include <websocketpp/transport/asio/security/none.hpp>
#include <websocketpp/transport/asio/security/tls.hpp>

template <typename base>
struct dummy_con : public base {
websocketpp::lib::error_code test() {
return this->translate_ec(websocketpp::lib::asio::error_code());
}
};

BOOST_AUTO_TEST_CASE( translated_ec_none ) {
dummy_con<websocketpp::transport::asio::basic_socket::connection> tscon;

// If the current configuration settings result in the library error type and the asio
// error type being the same, then the code should pass through natively. Otherwise
// we should get a generic pass through error.
if(websocketpp::lib::is_same<websocketpp::lib::error_code,websocketpp::lib::asio::error_code>::value) {
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::lib::error_code() );
} else {
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::transport::error::make_error_code(websocketpp::transport::error::pass_through) );
}
}

BOOST_AUTO_TEST_CASE( translated_ec_tls ) {
dummy_con<websocketpp::transport::asio::tls_socket::connection> tscon;

// If the current configuration settings result in the library error type and the asio
// error type being the same, then the code should pass through natively. Otherwise
// we should get a generic pass through error.
if(websocketpp::lib::is_same<websocketpp::lib::error_code,websocketpp::lib::asio::error_code>::value) {
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::lib::error_code() );
} else {
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::transport::error::make_error_code(websocketpp::transport::error::pass_through) );
}
}
2 changes: 2 additions & 0 deletions websocketpp/common/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ namespace lib {

#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_
using std::aligned_storage;
using std::is_same;
#else
using boost::aligned_storage;
using boost::is_same;
#endif

} // namespace lib
Expand Down
3 changes: 0 additions & 3 deletions websocketpp/transport/asio/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,6 @@ inline custom_alloc_handler<Handler> make_custom_alloc_handler(
template <typename config>
class endpoint;

typedef lib::function<void(lib::asio::error_code const &)>
socket_shutdown_handler;

typedef lib::function<void (lib::asio::error_code const & ec,
size_t bytes_transferred)> async_read_handler;

Expand Down
28 changes: 28 additions & 0 deletions websocketpp/transport/asio/connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,29 @@ class connection : public config::socket_type::socket_con_type {
return m_strand;
}

/// Get the internal transport error code for a closed/failed connection
/**
* Retrieves a machine readable detailed error code indicating the reason
* that the connection was closed or failed. Valid only after the close or
* fail handler is called.
*
* Primarily used if you are using mismatched asio / system_error
* implementations such as `boost::asio` with `std::system_error`. In these
* cases the transport error type is different than the library error type
* and some WebSocket++ functions that return transport errors via the
* library error code type will be coerced into a catch all `pass_through`
* or `tls_error` error. This method will return the original machine
* readable transport error in the native type.
*
* @since 0.7.0
*
* @return Error code indicating the reason the connection was closed or
* failed
*/
lib::asio::error_code get_transport_ec() const {
return m_tec;
}

/// Initialize transport for reading
/**
* init_asio is called once immediately after construction to initialize
Expand Down Expand Up @@ -877,6 +900,7 @@ class connection : public config::socket_type::socket_con_type {
// 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);
m_tec = ec;

if (tec == transport::error::tls_error ||
tec == transport::error::pass_through)
Expand Down Expand Up @@ -1100,6 +1124,7 @@ class connection : public config::socket_type::socket_con_type {
// 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);
m_tec = ec;

if (tec == transport::error::tls_short_read) {
// TLS short read at this point is somewhat expected if both
Expand Down Expand Up @@ -1156,6 +1181,9 @@ class connection : public config::socket_type::socket_con_type {

std::vector<lib::asio::const_buffer> m_bufs;

/// Detailed internal error code
lib::asio::error_code m_tec;

// Handlers
tcp_init_handler m_tcp_pre_init_handler;
tcp_init_handler m_tcp_post_init_handler;
Expand Down
5 changes: 4 additions & 1 deletion websocketpp/transport/asio/security/base.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
* 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:
Expand Down Expand Up @@ -28,6 +28,7 @@
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
#define WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP

#include <websocketpp/common/asio.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/system_error.hpp>
Expand Down Expand Up @@ -63,6 +64,8 @@ namespace transport {
namespace asio {
namespace socket {

typedef lib::function<void(lib::asio::error_code const &)> shutdown_handler;

/**
* The transport::asio::socket::* classes are a set of security/socket related
* policies and support code for the ASIO transport types.
Expand Down
29 changes: 23 additions & 6 deletions websocketpp/transport/asio/security/none.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include <websocketpp/uri.hpp>

#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/transport/asio/security/base.hpp>

#include <websocketpp/common/asio.hpp>
Expand Down Expand Up @@ -240,7 +241,7 @@ class connection : public lib::enable_shared_from_this<connection> {
m_socket->cancel();
}

void async_shutdown(socket_shutdown_handler h) {
void async_shutdown(socket::shutdown_handler h) {
lib::asio::error_code ec;
m_socket->shutdown(lib::asio::ip::tcp::socket::shutdown_both, ec);
h(ec);
Expand All @@ -252,20 +253,36 @@ class connection : public lib::enable_shared_from_this<connection> {

/// Translate any security policy specific information about an error code
/**
* Translate_ec takes a boost error code and attempts to convert its value
* to an appropriate websocketpp error code. The plain socket policy does
* not presently provide any additional information so all errors will be
* reported as the generic transport pass_through error.
* Translate_ec takes an Asio error code and attempts to convert its value
* to an appropriate websocketpp error code. In the case that the Asio and
* Websocketpp error types are the same (such as using boost::asio and
* boost::system_error or using standalone asio and std::system_error the
* code will be passed through natively.
*
* In the case of a mismatch (boost::asio with std::system_error) a
* translated code will be returned. The plain socket policy does not have
* any additional information so all such errors will be reported as the
* generic transport pass_through error.
*
* @since 0.3.0
*
* @param ec The error code to translate_ec
* @return The translated error code
*/
lib::error_code translate_ec(lib::asio::error_code) {
template <typename ErrorCodeType>
lib::error_code translate_ec(ErrorCodeType) {
// We don't know any more information about this error so pass through
return make_error_code(transport::error::pass_through);
}

/// Overload of translate_ec to catch cases where lib::error_code is the
/// same type as lib::asio::error_code
lib::error_code translate_ec(lib::error_code ec) {
// We don't know any more information about this error, but the error is
// the same type as the one we are translating to, so pass through
// untranslated.
return ec;
}
private:
enum state {
UNINITIALIZED = 0,
Expand Down
37 changes: 28 additions & 9 deletions websocketpp/transport/asio/security/tls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ typedef lib::function<void(connection_hdl,lib::asio::ssl::stream<
typedef lib::function<lib::shared_ptr<lib::asio::ssl::context>(connection_hdl)>
tls_init_handler;

/// TLS enabled Boost ASIO connection socket component
/// TLS enabled Asio connection socket component
/**
* transport::asio::tls_socket::connection implements a secure connection socket
* component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
Expand Down Expand Up @@ -315,26 +315,31 @@ class connection : public lib::enable_shared_from_this<connection> {
get_raw_socket().cancel();
}

void async_shutdown(socket_shutdown_handler callback) {
void async_shutdown(socket::shutdown_handler callback) {
m_socket->async_shutdown(callback);
}

/// Translate any security policy specific information about an error code
/**
* Translate_ec takes a boost error code and attempts to convert its value
* to an appropriate websocketpp error code. Any error that is determined to
* be related to TLS but does not have a more specific websocketpp error
* code is returned under the catch all error "tls_error".
* Translate_ec takes an Asio error code and attempts to convert its value
* to an appropriate websocketpp error code. In the case that the Asio and
* Websocketpp error types are the same (such as using boost::asio and
* boost::system_error or using standalone asio and std::system_error the
* code will be passed through natively.
*
* Non-TLS related errors are returned as the transport generic pass_through
* error.
* In the case of a mismatch (boost::asio with std::system_error) a
* translated code will be returned. Any error that is determined to be
* related to TLS but does not have a more specific websocketpp error code
* is returned under the catch all error `tls_error`. Non-TLS related errors
* are returned as the transport generic error `pass_through`
*
* @since 0.3.0
*
* @param ec The error code to translate_ec
* @return The translated error code
*/
lib::error_code translate_ec(boost::system::error_code ec) {
template <typename ErrorCodeType>
lib::error_code translate_ec(ErrorCodeType ec) {
if (ec.category() == lib::asio::error::get_ssl_category()) {
if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
return make_error_code(transport::error::tls_short_read);
Expand All @@ -349,6 +354,20 @@ class connection : public lib::enable_shared_from_this<connection> {
return make_error_code(transport::error::pass_through);
}
}

/// Overload of translate_ec to catch cases where lib::error_code is the
/// same type as lib::asio::error_code
lib::error_code translate_ec(lib::error_code ec) {
// Normalize the tls_short_read error as it is used by the library and
// needs a consistent value. All other errors pass through natively.
// TODO: how to get the SSL category from std::error?
/*if (ec.category() == lib::asio::error::get_ssl_category()) {
if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
return make_error_code(transport::error::tls_short_read);
}
}*/
return ec;
}
private:
socket_type::handshake_type get_handshake_type() {
if (m_is_server) {
Expand Down

0 comments on commit fb829e0

Please sign in to comment.