diff --git a/.travis.yml b/.travis.yml index 027ac560e..0ee516e9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: cpp compiler: - gcc before_install: - - sudo apt-get install libboost-regex1.48-dev libboost-system1.48-dev libboost-thread1.48-dev libboost-test1.48-dev libboost-random1.48-dev -y + - sudo apt-get install libboost-chrono1.48-dev libboost-regex1.48-dev libboost-system1.48-dev libboost-thread1.48-dev libboost-test1.48-dev libboost-random1.48-dev -y env: global: - BOOST_INCLUDES=/usr/include diff --git a/CMakeLists.txt b/CMakeLists.txt index 827915602..0874b4eed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ project (websocketpp) cmake_minimum_required (VERSION 2.6) set (WEBSOCKETPP_MAJOR_VERSION 0) -set (WEBSOCKETPP_MINOR_VERSION 5) -set (WEBSOCKETPP_PATCH_VERSION 1) +set (WEBSOCKETPP_MINOR_VERSION 6) +set (WEBSOCKETPP_PATCH_VERSION 0) set (WEBSOCKETPP_VERSION ${WEBSOCKETPP_MAJOR_VERSION}.${WEBSOCKETPP_MINOR_VERSION}.${WEBSOCKETPP_PATCH_VERSION}) set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") @@ -70,7 +70,7 @@ if (BUILD_TESTS OR BUILD_EXAMPLES) ############ Compiler specific setup set (WEBSOCKETPP_PLATFORM_LIBS "") - set (WEBSOCKETPP_PLATFORM_TSL_LIBS "") + set (WEBSOCKETPP_PLATFORM_TLS_LIBS "") set (WEBSOCKETPP_BOOST_LIBS "") # VC9 and C++11 reasoning @@ -111,7 +111,7 @@ if (BUILD_TESTS OR BUILD_EXAMPLES) # g++ if (CMAKE_COMPILER_IS_GNUCXX) set (WEBSOCKETPP_PLATFORM_LIBS pthread rt) - set (WEBSOCKETPP_PLATFORM_TSL_LIBS ssl crypto) + set (WEBSOCKETPP_PLATFORM_TLS_LIBS ssl crypto) set (WEBSOCKETPP_BOOST_LIBS system thread) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") if (NOT APPLE) @@ -133,7 +133,7 @@ if (BUILD_TESTS OR BUILD_EXAMPLES) else() set (WEBSOCKETPP_PLATFORM_LIBS pthread) endif() - set (WEBSOCKETPP_PLATFORM_TSL_LIBS ssl crypto) + set (WEBSOCKETPP_PLATFORM_TLS_LIBS ssl crypto) set (WEBSOCKETPP_BOOST_LIBS system thread) set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++0x -stdlib=libc++") # todo: is libc++ really needed here? if (NOT APPLE) diff --git a/Doxyfile b/Doxyfile index ff9724267..b9ff53dc0 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,9 +32,7 @@ PROJECT_NAME = "websocketpp" # This could be handy for archiving the generated documentation or # if some version control system is used. - -PROJECT_NUMBER = "0.5.1" - +PROJECT_NUMBER = "0.6.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer diff --git a/changelog.md b/changelog.md index ded965084..986245bf8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,50 @@ HEAD +0.6.0 +- MINOR BREAKING TRANSPORT POLICY CHANGE: Custom transport policies will now be + required to include a new method `void set_uri(uri_ptr u)`. An implementation + is not required. The stub transport policy includes an example stub method + that can be added to any existing custom transport policy to fulfill this + requirement. This does not affect anyone using the bundled transports or + configs. +- MINOR BREAKING SOCKET POLICY CHANGE: Custom asio transport socket policies + will now be required to include a new method `void set_uri(uri_ptr u)`. Like + with the transport layer, an implementation is not required. This does not + affect anyone using the bundled socket policies. +- MINOR BREAKING DEPENDENCY CHANGE: When using Boost versions greater than or + equal to 1.49 in C++03 mode, `libboost-chrono` is needed now instead of + `libboost-date_time`. Users with C++11 compilers or using Boost versions 1.48 + and earlier are not affected. Note: This change affects the bundled unit test + suite. +- Feature: WebSocket++ Asio transport policy can now be used with the standalone + version of Asio (1.8.0+) when a C++11 compiler and standard library are + present. This means that it is possible now to use WebSocket++'s Asio + transport entirely without Boost. Thank you Robert Seiler for proof of concept + code that was used as a guide for this implementation. Fixes #324 +- Feature: Adds a vectored/scatter-gather write handler to the iostream + transport. +- Feature: Adds the ability to defer sending an HTTP response until sometime + after the `http_handler` is run. This allows processing of long running http + handlers to defer their response until it is ready without blocking the + network thread. references #425 +- Improvement: `echo_server_tls` has been update to demonstrate how to configure + it for Mozilla's recommended intermediate and modern TLS security profiles. +- Improvement: `endpoint::set_timer` now uses a steady clock provided by + `boost::chrono` or `std::chrono` where available instead of the non-monotonic + system clock. Thank you breyed for reporting. fixes #241 +- Improvement: Outgoing TLS connections to servers using the SNI extension to + choose a certificate will now work. Thank you moozzyk for reporting. + Fixes #400 +- Improvement: Removes an unnecessary mutex lock in `get_con_from_hdl`. +- Cleanup: Asio transport policy has been refactored to remove many Boost + dependencies. On C++03 compilers the `boost::noncopyable` dependency has been + removed and the `boost::date_time` dependency has been replaced with the newer + `boost::chrono` when possible. On C++11 compilers the `boost::aligned_storage` + and `boost::date_time` dependencies are gone, replaced with equivalent C++11 + standard library features. +- Bug: Fixes a potential dangling pointer and inconsistent error message + handling in `websocketpp::exception`. #432 Thank you Tom Swirly for the fix. + 0.5.1 - 2015-02-27 - Bug: Fixes an issue where some frame data was counted against the max header size limit, resulting in connections that included a lot of frame data diff --git a/examples/echo_server/echo_server.cpp b/examples/echo_server/echo_server.cpp index 5ec576f94..5ae78ad39 100644 --- a/examples/echo_server/echo_server.cpp +++ b/examples/echo_server/echo_server.cpp @@ -19,6 +19,13 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { << " 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 (const websocketpp::lib::error_code& e) { diff --git a/examples/echo_server_tls/dh.pem b/examples/echo_server_tls/dh.pem new file mode 100644 index 000000000..6b1c74d43 --- /dev/null +++ b/examples/echo_server_tls/dh.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAqxMGKZB8YNV8WQnbJWwwwmifc+PfVRtd1FN5v5aQSsf6dpjX3Zlh +N1NmgecsQyg4u2EWe4Umta10QzCgYaxf6QdTCg7iprLzFNw7IvWYbQ6du12NMGDr +hmwA6KQKwbTgPL6mSlSlcK2wTP2FzxDTNffFu10cB/6Fj4kdQjPG0c1Koz/z7OOq +BuDElJLClS8rjp3z1xvrc7gX95dFa2KaKgOAYDkpe8tfHRhHfJeIVS/whH9hzx6r +OBg+E5K9JyvayrUoKgPeptRKCqo8A4YevtMLpRxMup0nMUgAIv6+BGTwPAFpwgl/ +8UIVcvjh1v95PwGDM/Q8yvIBJznBYk/e2wIBAg== +-----END DH PARAMETERS----- diff --git a/examples/echo_server_tls/echo_server_tls.cpp b/examples/echo_server_tls/echo_server_tls.cpp index 8408ebccc..a260c9e4e 100644 --- a/examples/echo_server_tls/echo_server_tls.cpp +++ b/examples/echo_server_tls/echo_server_tls.cpp @@ -27,24 +27,66 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { } } +void on_http(server* s, websocketpp::connection_hdl hdl) { + server::connection_ptr con = s->get_con_from_hdl(hdl); + + con->set_body("Hello World!"); + con->set_status(websocketpp::http::status_code::ok); +} + std::string get_password() { return "test"; } -context_ptr on_tls_init(websocketpp::connection_hdl hdl) { +// See https://wiki.mozilla.org/Security/Server_Side_TLS for more details about +// the TLS modes. The code below demonstrates how to implement both the modern +enum tls_mode { + MOZILLA_INTERMEDIATE = 1, + MOZILLA_MODERN = 2 +}; + +context_ptr on_tls_init(tls_mode mode, websocketpp::connection_hdl hdl) { std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl; - context_ptr ctx = websocketpp::lib::make_shared(boost::asio::ssl::context::tlsv1); + std::cout << "using TLS mode: " << (mode == MOZILLA_MODERN ? "Mozilla Modern" : "Mozilla Intermediate") << std::endl; + context_ptr ctx = websocketpp::lib::make_shared(boost::asio::ssl::context::sslv23); try { - ctx->set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3 | - boost::asio::ssl::context::single_dh_use); + if (mode == MOZILLA_MODERN) { + // Modern disables TLSv1 + ctx->set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::no_tlsv1 | + boost::asio::ssl::context::single_dh_use); + } else { + ctx->set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::single_dh_use); + } ctx->set_password_callback(bind(&get_password)); ctx->use_certificate_chain_file("server.pem"); ctx->use_private_key_file("server.pem", boost::asio::ssl::context::pem); + + // Example method of generating this file: + // `openssl dhparam -out dh.pem 2048` + // Mozilla Intermediate suggests 1024 as the minimum size to use + // Mozilla Modern suggests 2048 as the minimum size to use. + ctx->use_tmp_dh_file("dh.pem"); + + std::string ciphers; + + if (mode == MOZILLA_MODERN) { + ciphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; + } else { + ciphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; + } + + if (SSL_CTX_set_cipher_list(ctx->native_handle() , ciphers.c_str()) != 1) { + std::cout << "Error setting cipher list" << std::endl; + } } catch (std::exception& e) { - std::cout << e.what() << std::endl; + std::cout << "Exception: " << e.what() << std::endl; } return ctx; } @@ -58,8 +100,8 @@ int main() { // Register our message handler echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2)); - echo_server.set_tls_init_handler(bind(&on_tls_init,::_1)); - + echo_server.set_http_handler(bind(&on_http,&echo_server,::_1)); + echo_server.set_tls_init_handler(bind(&on_tls_init,MOZILLA_INTERMEDIATE,::_1)); // Listen on port 9002 echo_server.listen(9002); diff --git a/examples/echo_server_tls/server.pem b/examples/echo_server_tls/server.pem index 9f679a49b..5196dcfe5 100644 --- a/examples/echo_server_tls/server.pem +++ b/examples/echo_server_tls/server.pem @@ -1,58 +1,55 @@ -----BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,A0ED66EF872A48A9 - -gXuvKojXzApVhhPVNdRliiajbC4PtwQG5c8TA7JADLgwOR7o9t6KtXEr37bDRpvB -9aO9P+SJaK5OOp3XKPGthOdqv+tvCRTlmzmC8GjPLBX389DWT2xoGu7JkGwDtdSm -rnF49Rlp5bfjpACk5xKNiKeDo1CWfeEJzw9Kto0g+5eMaEdors64oPzjXs3geA2g -TxCJSHv9qSX6++pCLKKCUTbyzidAxV/Zb0AAubt5V40QKqX4HhSwwstFnTaX3tlb -3QOdY+y04VIkM6d7qN5W8M7NzRkMpZ1qBpQcUMpkhQcRzWP2wub5AAff9D2GntRd -4Dz1vn3u41U3Okdr0CNj+iH7byCzuokoAhk6ZQEN6WB+GTpGgfBXdtUZrfpb0MKm -UNYP5AF2AmUqJRXhViTDVtu/V2tHF3LGuNT+W2Dz+spFZEq0byEO0N858eR0dikc -6jOASvNQbSwD0+mkgBC1gXKKU3ngj2gpJUwljeACdWFd8N2egrZfyI05CmX7vPNC -NXbs7k2buWNdjP4/D8IM+HDVidWzQa/kG/qokXKqllem9Egg37lUucwnP3cX2/Hw -U2mfaBWzeZtqc+GqRp08rYIql+Reai3sUYlQMnNk01prVY47UQb+dxuqjaxGV5Xx -Xkx0s2mfQnNRjL4S7Hjhqelufi6GpkCQ2EGsPpA+6K1ztZ0ame9Q2BE1SXeM/6vU -rxT5nRrCxueyXAyQSGcqMX9//gSeK8WWBqG/c1IAMVDa0NWrJeOJhSziE+ta3B0m -bHAPBY6vh0iB3lLdRlbUOPbC6R1TpxMOs+6Vbs2+OTifFpvOVymEoZq/nroyg68P -vn5uCKogwWA7o8EArf/UTlIwWJmH9bgILdZKld4wMel2HQg16RDzm+mEXAJi52a/ -FC+fgfphdxltmUJ+rqOyR4AHULjaTWUQqTIB6sdlzgmES1nXAiE71zX//KFqomar -O60SPPk3C1bs0x5DsvmGJa8SIfDhyd+D7NPyqwEKqrZsaotYGklNkfqxa6pa8mrc -ejxquW1PK4FvBk26+osu5a90Jih0PcQM7DUMMr2WHdTiMSXWAiK2ToYF8Itt25Qv -Cd0CsSYw9CJkXNr1u1+mObheaY9QYOmztnSJLy4ZO2JsMhqNwuAueIcwmhXOREq7 -kzlnGMgJcuSeAS/OBNj8Zgx0c7QQ0kzc+YmnOCsqoMtPsu/CsXJ4iJiM3Tki/2jT -bywrTiQwE6R3a/87GREOREX+WLicZBWX3k9/4tBL5XSe1p5wPpuIRQUDvAGNfNHP -JN7kujDF4SehilF1qtvCygAwvxHFDj+EwhXKNDKJzoZZIM15rAk3k92n2j6nz1qH -a3xOU05yydOlO6F6w51I1QoDddmkzCRNB0TeO3D6rekHsCK1aDWmC+qRcm2ZFtVz -sY6fdZN2NEmMQokIh9Opi1f8CSYSizPESMzdu2SF0xVO9n/IGIkn1ksK04O2BZo0 -X3LBPHLfCRsQNY1eF17bj07fYU2oPZKs/XzJiwxkqK6LFvpeAVaYrtg9fqRO/UVe -QhUIj3BL550ocEpa15xLehLrmwzYiW5zwGjSHQ4EgZluGLCwyKGTh4QswEJRA9Rt +MIIEowIBAAKCAQEA77v5VbFaq+geVd/RCLXoKTWj/LgXGjiOXBpJZca3o5edi7N1 +Ej41/13jfZ4OxgFIRFAALd+fU4NsObXzskth3rJLmJjUtGajRIPqF2IUVjFMmybQ +9dUDSqPzd/bG+ul0DP7DNuiBcLC8XNC3/kchPUzVq9gNDk56jXn87kMoIkVXBWQS +Rwk0yCtELSfwxxRCrGKgQDfXnRdwmkPe42DmKtdGksXJlUhP1UVTAXaSw1Nz64LV +9bJje/eoDsSe4OxifVVToE6ZxIW+r1jVgrcupeYXQrSHIztl2U/rx2JkGMN0eS+P +qrsJmkmRBN1cpgmvSV7WoM3hj6Eq71z4Dfv6EwIDAQABAoIBAEXAL19bZsI1mv3p +TOx34MB8tuXEuhQK+ICbtVdDZhLW/iOzZxCTwSo3qwTVg/7gSKJ3lFXEhprJ1idE +ZU8u157vyUbw0JJceoxoxSdghgI9/cf2lz2vaHHDGgeBaYt/eSB+z1WeeGrNQUDQ +CXiWQXmQbWq+Ra4v70BSieDY8UhmzCTRhvfZV80vaY/4SnHxJ9C8Nu8/An7U3pwq +ccfTaWMp3Q5ztmEnExF6/b1SUsnI8rzFovw/4C50AU09N/Z6zZVFsXgJAT2Kd1Tx +HfP1vUkWZ/TJ6kcmVIV9U3uMcedBD8Rb/3M0Qdp+eymQ+1oYQ3K3/a2RlUQIeXzf +hRbE4wECgYEA/WyULrwBsvSMPqK0K9pZpv921Yl6d4bGnpxKze7ltc9o5/LsU35q +8u29Es7cPyhyUDOsPW8v2CNjw54t+eFGsu59bBvXZNz5gB7hTUEyLntB24cO5PmN +kVExcMDFC6NgPaauBtUhODievKJ2C8P7sRJDAVKE9KkavDddU7nSEjMCgYEA8ivG +AqwNXYsiIFjAo4uj3kuBFt8LU/q4m6GXI+9qyxfiJjuF1dROx5qaQGiE85VaBFUu +gpbnjZH7s+BBgoaQhrB4i6mztljdN1KKuAlnjhB+Fywg8m8N9lAi3wIaxnIEqDkU +vFiIFPTBCk0U7IZ/OyCrKPQTE9ApWHfBwA/vWKECgYBQixrJg61SkBCms5Vpvprx +zY2aLniC1o33yRqpOr09PG9OENH1c19QWCjYenBbjmJOhS2/1L+zBQRnHrXkDion +Ik8wdeTORMDzbF0U7ZyiU0BKIjGkqn/I6LI68LlvinxC+9+hgkltkek5cLTt5lrv +Gyu6ltx02e4KVdpOiuduKwKBgQC3U0PWigCkK8ttyUIrjG5Evcu/UKH2tPpDdpQ/ +8+JYVIAyiSTLtqjcmcDjuTvMWeeHGCTZXvtzRGvSw5VUBiIqlDTtJU6SX7s3QhkZ +MKVf+kQ5roJShJeBOzDquWEjkPTNlEiKPErn8lCgR7HrS/XNAPIRUpOOkCp8ekwF +5Qo/gQKBgAue3TeCeIzxh4PxSAk39Uaj+AaLvlYg8C+ucEodCNQZnJ7jOO0hHkfT +NvZBes0VukCGlBLOGRQtC3kgtyrerscnDiZOhdnJrhPIdNPC0k/p/uAfh7+C5Sxm +rCEL0S7kORdEjlIGd5j6ZEN/HZe8FyOYSjbFAoJSlAId9WMSxycQ -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIIE0DCCA7igAwIBAgIJAM5MuKJezXq0MA0GCSqGSIb3DQEBBQUAMIGgMQswCQYD -VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xGDAW -BgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0KysxFjAU -BgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3RlckB6 -YXBob3lkLmNvbTAeFw0xMTExMTUyMTIwMDZaFw0xMjExMTQyMTIwMDZaMIGgMQsw -CQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28x -GDAWBgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0Kysx -FjAUBgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3Rl -ckB6YXBob3lkLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANR0 -tdwAnIB8I9qRZ7QbzEWY95RpM7GIn0u/9oH90PzdHiE0rXSkKT+yw3XUzH0iw5t0 -5dEwSC+srSP5Vm4cA6kXc94agVVaPW89tGcdP4fHptCruSrzQsDXELCPl5UUvMpA -YUcGisdXYPN/EeOoqb9wKWxoW5mREsyyeWWS89fYN5qU/d0QpbSvEWghqLbL/ZS2 -hOlXT9LufOeA+vHiV1/T/h5xC7ecIH02YDQw1EnqxbPmkLPcWThztLS9FiufNDRM -Rhcoaj2b9VDHvDwdbeA0T5v5qNdG34LaapYOelxzQMOtM0f9Dgqehodyxl2qm9mR -lq432dlOEzDnVCPNHwECAwEAAaOCAQkwggEFMB0GA1UdDgQWBBTTPKfNMnKOykhv -+vKS7vql5JsMyzCB1QYDVR0jBIHNMIHKgBTTPKfNMnKOykhv+vKS7vql5JsMy6GB -pqSBozCBoDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQH -EwdDaGljYWdvMRgwFgYDVQQKEw9aYXBob3lkIFN0dWRpb3MxFDASBgNVBAsTC1dl -YlNvY2tldCsrMRYwFAYDVQQDEw1QZXRlciBUaG9yc29uMSQwIgYJKoZIhvcNAQkB -FhV3ZWJtYXN0ZXJAemFwaG95ZC5jb22CCQDOTLiiXs16tDAMBgNVHRMEBTADAQH/ -MA0GCSqGSIb3DQEBBQUAA4IBAQB+SH0s/hrv5VYqgX6SNLzxdSLvCVsUkCdTpxwY -wOJ84XmYcXDMhKDtZqLtOtN6pfEwVusFlC9mkieuunztCnWNmsSG83RuljJPjFSi -1d4Id4bKEQkQ4cfnjoHKivRrViWLnxuNnLzC6tpyGH/35kKWhhr6T58AXerFgVw3 -mHvLPTr1DuhdAZA0ZuvuseVAFFAjI3RetSySwHJE3ak8KswDVfLi6E3XxMVsIWTS -/iFsC2WwoZQlljya2V/kRYIhu+uCdqJ01wunn2BvmURPSgr4GTBF0FQ9JGpNbXxM -TAU7oQJgyFc5sCcuEgPTO0dWVQTvdZVgay4tkmduKDRkmJBF +MIIEpzCCA4+gAwIBAgIJAMlCHvwnJ+F/MA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCSUwxEDAOBgNVBAcTB0NoaWNhZ28xGTAXBgNVBAoT +EFdlYlNvY2tldCsrIERlbW8xIjAgBgNVBAMTGXV0aWxpdGllcy53ZWJzb2NrZXRw +cC5vcmcxJjAkBgkqhkiG9w0BCQEWF3dlYnNvY2tldHBwQHphcGhveWQuY29tMB4X +DTE1MDQyNTE1NTk1M1oXDTE4MDMyODE1NTk1M1owgZMxCzAJBgNVBAYTAlVTMQsw +CQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEZMBcGA1UEChMQV2ViU29ja2V0 +KysgRGVtbzEiMCAGA1UEAxMZdXRpbGl0aWVzLndlYnNvY2tldHBwLm9yZzEmMCQG +CSqGSIb3DQEJARYXd2Vic29ja2V0cHBAemFwaG95ZC5jb20wggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDvu/lVsVqr6B5V39EItegpNaP8uBcaOI5cGkll +xrejl52Ls3USPjX/XeN9ng7GAUhEUAAt359Tg2w5tfOyS2HeskuYmNS0ZqNEg+oX +YhRWMUybJtD11QNKo/N39sb66XQM/sM26IFwsLxc0Lf+RyE9TNWr2A0OTnqNefzu +QygiRVcFZBJHCTTIK0QtJ/DHFEKsYqBAN9edF3CaQ97jYOYq10aSxcmVSE/VRVMB +dpLDU3PrgtX1smN796gOxJ7g7GJ9VVOgTpnEhb6vWNWCty6l5hdCtIcjO2XZT+vH +YmQYw3R5L4+quwmaSZEE3VymCa9JXtagzeGPoSrvXPgN+/oTAgMBAAGjgfswgfgw +HQYDVR0OBBYEFAWNBET93ZzSukXGkuGb93CfmUkDMIHIBgNVHSMEgcAwgb2AFAWN +BET93ZzSukXGkuGb93CfmUkDoYGZpIGWMIGTMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCSUwxEDAOBgNVBAcTB0NoaWNhZ28xGTAXBgNVBAoTEFdlYlNvY2tldCsrIERl +bW8xIjAgBgNVBAMTGXV0aWxpdGllcy53ZWJzb2NrZXRwcC5vcmcxJjAkBgkqhkiG +9w0BCQEWF3dlYnNvY2tldHBwQHphcGhveWQuY29tggkAyUIe/Ccn4X8wDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA2DRQSDmo2Q5t8MEjp6jddRIFts5t +BDBWPSwJ0qvmheFt53MuLds2hhvMn42WGrKtVJiLiDyOWuUZ41REJZb1uaissUuY +r9pLuP5QLdibx7+/30iDEY0OGTgtSTfgwNx8bIApBSHoZEN3/HaikqplBng2+6u/ +kTe6UnRrBJ+8JOGl+duhCXNPeSyLa2NcrxE9XpWC/k1kC9MTUF+2NuqCtK3zO8ci +p0mqARWDSrEBYISh3dAOgDFrcX6zj+0MK+iswu3ijEdItGAjxjlQ2t4XT1T/CDbc +ysHg04TJw7v682+v124GrnrAPYUK34OK8kFVJ60SNYRUi7euOCdTM4Lwkw== -----END CERTIFICATE----- + diff --git a/examples/scratch_server/scratch_server.cpp b/examples/scratch_server/scratch_server.cpp index 32c395f16..e37a58929 100644 --- a/examples/scratch_server/scratch_server.cpp +++ b/examples/scratch_server/scratch_server.cpp @@ -84,7 +84,9 @@ int main(int argc, char * argv[]) { echo_server.init_asio(); // Register our message handler - echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2)); + using websocketpp::lib::placeholders::_1; + using websocketpp::lib::placeholders::_2; + echo_server.set_message_handler(bind(&on_message,&echo_server,_1,_2)); // Listen on port 9002 echo_server.listen(9002); diff --git a/examples/telemetry_client/telemetry_client.cpp b/examples/telemetry_client/telemetry_client.cpp index 658fb3d46..6a57da15d 100644 --- a/examples/telemetry_client/telemetry_client.cpp +++ b/examples/telemetry_client/telemetry_client.cpp @@ -30,9 +30,9 @@ class telemetry_client { // Bind the handlers we are using using websocketpp::lib::placeholders::_1; using websocketpp::lib::bind; - m_client.set_open_handler(bind(&telemetry_client::on_open,this,::_1)); - m_client.set_close_handler(bind(&telemetry_client::on_close,this,::_1)); - m_client.set_fail_handler(bind(&telemetry_client::on_fail,this,::_1)); + m_client.set_open_handler(bind(&telemetry_client::on_open,this,_1)); + m_client.set_close_handler(bind(&telemetry_client::on_close,this,_1)); + m_client.set_fail_handler(bind(&telemetry_client::on_fail,this,_1)); } // This method will block until the connection is complete diff --git a/examples/telemetry_server/telemetry_server.cpp b/examples/telemetry_server/telemetry_server.cpp index 80a791343..7c45ae590 100644 --- a/examples/telemetry_server/telemetry_server.cpp +++ b/examples/telemetry_server/telemetry_server.cpp @@ -46,9 +46,9 @@ class telemetry_server { // Bind the handlers we are using using websocketpp::lib::placeholders::_1; using websocketpp::lib::bind; - m_endpoint.set_open_handler(bind(&telemetry_server::on_open,this,::_1)); - m_endpoint.set_close_handler(bind(&telemetry_server::on_close,this,::_1)); - m_endpoint.set_http_handler(bind(&telemetry_server::on_http,this,::_1)); + m_endpoint.set_open_handler(bind(&telemetry_server::on_open,this,_1)); + m_endpoint.set_close_handler(bind(&telemetry_server::on_close,this,_1)); + m_endpoint.set_http_handler(bind(&telemetry_server::on_http,this,_1)); } void run(std::string docroot, uint16_t port) { diff --git a/readme.md b/readme.md index a9551373d..18716d4fa 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,12 @@ -WebSocket++ (0.5.1) +WebSocket++ (0.6.0) ========================== WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket Protocol. It allows integrating WebSocket client and server functionality into C++ programs. It uses interchangeable network transport modules including one -based on C++ iostreams and one based on Boost Asio. +based on raw char buffers, one based on C++ iostreams, and one based on Asio +(either via Boost or standalone). End users can write additional transport +policies to support other networking or event libraries as needed. Major Features ============== @@ -13,7 +15,7 @@ Major Features * Message/event based interface * Supports secure WebSockets (TLS), IPv6, and explicit proxies. * Flexible dependency management (C++11 Standard Library or Boost) -* Interchangeable network transport modules (iostream and Boost Asio) +* Interchangeable network transport modules (raw, iostream, Asio, or custom) * Portable/cross platform (Posix/Windows, 32/64bit, Intel/ARM/PPC) * Thread-safe @@ -31,6 +33,8 @@ http://www.zaphoyd.com/websocketpp/manual/ **GitHub Repository** https://github.com/zaphoyd/websocketpp/ +GitHub pull requests should be submitted to the `develop` branch. + **Announcements Mailing List** http://groups.google.com/group/websocketpp-announcements/ diff --git a/test/connection/connection.cpp b/test/connection/connection.cpp index 0c67bd193..5491ab1d0 100644 --- a/test/connection/connection.cpp +++ b/test/connection/connection.cpp @@ -167,6 +167,15 @@ void http_func(server* s, websocketpp::connection_hdl hdl) { con->set_status(websocketpp::http::status_code::ok); } +void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl hdl) { + *deferred = true; + + server::connection_ptr con = s->get_con_from_hdl(hdl); + + websocketpp::lib::error_code ec = con->defer_http_response(); + BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); +} + void check_on_fail(server* s, websocketpp::lib::error_code ec, bool & called, websocketpp::connection_hdl hdl) { @@ -223,6 +232,44 @@ BOOST_AUTO_TEST_CASE( http_request ) { BOOST_CHECK_EQUAL(run_server_test(s,input), output); } +BOOST_AUTO_TEST_CASE( deferred_http_request ) { + std::string input = "GET /foo/bar HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n"; + std::string output = "HTTP/1.1 200 OK\r\nContent-Length: 8\r\nServer: "; + output+=websocketpp::user_agent; + output+="\r\n\r\n/foo/bar"; + + server s; + server::connection_ptr con; + bool deferred = false; + s.set_http_handler(bind(&defer_http_func,&s, &deferred,::_1)); + + s.clear_access_channels(websocketpp::log::alevel::all); + s.clear_error_channels(websocketpp::log::elevel::all); + + std::stringstream ostream; + s.register_ostream(&ostream); + + con = s.get_connection(); + con->start(); + + BOOST_CHECK(!deferred); + BOOST_CHECK_EQUAL(ostream.str(), ""); + con->read_some(input.data(),input.size()); + BOOST_CHECK(deferred); + BOOST_CHECK_EQUAL(ostream.str(), ""); + + con->set_body(con->get_resource()); + con->set_status(websocketpp::http::status_code::ok); + + websocketpp::lib::error_code ec; + s.send_http_response(con->get_handle(),ec); + BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); + BOOST_CHECK_EQUAL(ostream.str(), output); + con->send_http_response(ec); + BOOST_CHECK_EQUAL(ec, make_error_code(websocketpp::error::invalid_state)); + +} + BOOST_AUTO_TEST_CASE( request_no_server_header ) { std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n"; std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nUpgrade: websocket\r\n\r\n"; diff --git a/test/endpoint/endpoint.cpp b/test/endpoint/endpoint.cpp index 08b0560b5..34764d1bd 100644 --- a/test/endpoint/endpoint.cpp +++ b/test/endpoint/endpoint.cpp @@ -57,6 +57,35 @@ BOOST_AUTO_TEST_CASE( initialize_server_asio_external ) { s.init_asio(&ios); } +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ +BOOST_AUTO_TEST_CASE( move_construct_server_core ) { + websocketpp::server s1; + + websocketpp::server s2(std::move(s1)); +} + +/* +// temporary disable because library doesn't pass +BOOST_AUTO_TEST_CASE( emplace ) { + std::stringstream out1; + std::stringstream out2; + + std::vector> v; + + v.emplace_back(); + v.emplace_back(); + + v[0].get_alog().set_ostream(&out1); + v[0].get_alog().set_ostream(&out2); + + v[0].get_alog().write(websocketpp::log::alevel::app,"devel0"); + v[1].get_alog().write(websocketpp::log::alevel::app,"devel1"); + BOOST_CHECK( out1.str().size() > 0 ); + BOOST_CHECK( out2.str().size() > 0 ); +}*/ + +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + struct endpoint_extension { endpoint_extension() : extension_value(5) {} diff --git a/test/logger/basic.cpp b/test/logger/basic.cpp index de51e5ad2..a73053575 100644 --- a/test/logger/basic.cpp +++ b/test/logger/basic.cpp @@ -34,6 +34,8 @@ #include #include +typedef websocketpp::log::basic basic_access_log_type; + BOOST_AUTO_TEST_CASE( is_token_char ) { typedef websocketpp::log::basic error_log; @@ -79,3 +81,65 @@ BOOST_AUTO_TEST_CASE( basic_concurrency ) { //std::cout << "|" << out.str() << "|" << std::endl; BOOST_CHECK( out.str().size() > 0 ); } + + +BOOST_AUTO_TEST_CASE( copy_constructor ) { + std::stringstream out; + + basic_access_log_type logger1(0xffffffff,&out); + basic_access_log_type logger2(logger1); + + logger2.set_channels(0xffffffff); + logger2.write(websocketpp::log::alevel::devel,"devel"); + BOOST_CHECK( out.str().size() > 0 ); +} + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ +BOOST_AUTO_TEST_CASE( move_constructor ) { + std::stringstream out; + + basic_access_log_type logger1(0xffffffff,&out); + basic_access_log_type logger2(std::move(logger1)); + + logger2.set_channels(0xffffffff); + logger2.write(websocketpp::log::alevel::devel,"devel"); + BOOST_CHECK( out.str().size() > 0 ); +} + +// Emplace requires move assignment, which logger doesn't support right now +// due to const members. This is pretty irritating and will probably result in +// the const members being removed. For now though this test will fail to +// compile +/*BOOST_AUTO_TEST_CASE( emplace ) { + std::stringstream out1; + std::stringstream out2; + + std::vector v; + + v.emplace_back(websocketpp::log::level(0xffffffff),&out1); + v.emplace_back(websocketpp::log::level(0xffffffff),&out2); + + v[0].set_channels(0xffffffff); + v[1].set_channels(0xffffffff); + v[0].write(websocketpp::log::alevel::devel,"devel"); + v[1].write(websocketpp::log::alevel::devel,"devel"); + BOOST_CHECK( out1.str().size() > 0 ); + BOOST_CHECK( out2.str().size() > 0 ); +}*/ +#endif // #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + +// As long as there are const member variables these can't exist +// These remain commented as they are useful for testing the deleted operators +/*BOOST_AUTO_TEST_CASE( copy_assign ) { + basic_access_log_type logger1; + basic_access_log_type logger2; + + logger2 = logger1; +} + +BOOST_AUTO_TEST_CASE( move_assign ) { + basic_access_log_type logger1; + basic_access_log_type logger2; + + logger2 = std::move(logger1); +}*/ diff --git a/test/transport/SConscript b/test/transport/SConscript index 74ec781ef..71b31b6c5 100644 --- a/test/transport/SConscript +++ b/test/transport/SConscript @@ -11,7 +11,7 @@ Import('tls_libs') env = env.Clone () env_cpp11 = env_cpp11.Clone () -BOOST_LIBS = boostlibs(['unit_test_framework','system','thread','random'],env) + [platform_libs] + [tls_libs] +BOOST_LIBS = boostlibs(['unit_test_framework','system','thread','random','chrono'],env) + [platform_libs] + [tls_libs] objs = env.Object('boost_integration.o', ["integration.cpp"], LIBS = BOOST_LIBS) prgs = env.Program('test_boost_integration', ["boost_integration.o"], LIBS = BOOST_LIBS) diff --git a/test/transport/asio/SConscript b/test/transport/asio/SConscript index 6cae57c07..ea0526b3a 100644 --- a/test/transport/asio/SConscript +++ b/test/transport/asio/SConscript @@ -11,7 +11,7 @@ Import('tls_libs') env = env.Clone () env_cpp11 = env_cpp11.Clone () -BOOST_LIBS = boostlibs(['unit_test_framework','system','thread'],env) + [platform_libs] + [tls_libs] +BOOST_LIBS = boostlibs(['unit_test_framework','system','thread','chrono'],env) + [platform_libs] + [tls_libs] objs = env.Object('base_boost.o', ["base.cpp"], LIBS = BOOST_LIBS) objs += env.Object('timers_boost.o', ["timers.cpp"], LIBS = BOOST_LIBS) diff --git a/test/transport/integration.cpp b/test/transport/integration.cpp index 1d2e7e795..f886fbf26 100644 --- a/test/transport/integration.cpp +++ b/test/transport/integration.cpp @@ -607,3 +607,11 @@ BOOST_AUTO_TEST_CASE( pause_reading ) { BOOST_AUTO_TEST_CASE( server_connection_cleanup ) { server_tls s; } + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ +BOOST_AUTO_TEST_CASE( move_construct_transport ) { + server s1; + + server s2(std::move(s1)); +} +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ diff --git a/test/transport/iostream/connection.cpp b/test/transport/iostream/connection.cpp index 49465d6ba..2a4501352 100644 --- a/test/transport/iostream/connection.cpp +++ b/test/transport/iostream/connection.cpp @@ -117,7 +117,7 @@ struct stub_con : public iostream_con { indef_read_size = num_bytes; indef_read_buf = buf; indef_read_len = len; - + indef_read(); } @@ -138,7 +138,7 @@ struct stub_con : public iostream_con { void handle_indef(websocketpp::lib::error_code const & e, size_t amt_read) { ec = e; indef_read_total += amt_read; - + indef_read(); } @@ -203,6 +203,15 @@ websocketpp::lib::error_code write_handler(std::string & o, websocketpp::connect return websocketpp::lib::error_code(); } +websocketpp::lib::error_code vector_write_handler(std::string & o, websocketpp::connection_hdl, std::vector const & bufs) { + std::vector::const_iterator it; + for (it = bufs.begin(); it != bufs.end(); it++) { + o += std::string((*it).buf, (*it).len); + } + + return websocketpp::lib::error_code(); +} + websocketpp::lib::error_code write_handler_error(websocketpp::connection_hdl, char const *, size_t) { return make_error_code(websocketpp::transport::error::general); } @@ -249,7 +258,7 @@ BOOST_AUTO_TEST_CASE( async_write_vector_0_write_handler ) { std::string output; stub_con::ptr con(new stub_con(true,alogger,elogger)); - + con->set_write_handler(websocketpp::lib::bind( &write_handler, websocketpp::lib::ref(output), @@ -354,6 +363,31 @@ BOOST_AUTO_TEST_CASE( async_write_vector_2_write_handler ) { BOOST_CHECK_EQUAL( output, "foobar" ); } +BOOST_AUTO_TEST_CASE( async_write_vector_2_vector_write_handler ) { + std::string output; + + stub_con::ptr con(new stub_con(true,alogger,elogger)); + con->set_vector_write_handler(websocketpp::lib::bind( + &vector_write_handler, + websocketpp::lib::ref(output), + websocketpp::lib::placeholders::_1, + websocketpp::lib::placeholders::_2 + )); + + std::vector bufs; + + std::string foo = "foo"; + std::string bar = "bar"; + + bufs.push_back(websocketpp::transport::buffer(foo.data(),foo.size())); + bufs.push_back(websocketpp::transport::buffer(bar.data(),bar.size())); + + con->write(bufs); + + BOOST_CHECK( !con->ec ); + BOOST_CHECK_EQUAL( output, "foobar" ); +} + BOOST_AUTO_TEST_CASE( async_read_at_least_too_much ) { stub_con::ptr con(new stub_con(true,alogger,elogger)); @@ -483,7 +517,7 @@ BOOST_AUTO_TEST_CASE( async_read_at_least_read_some_indef ) { BOOST_CHECK( !con->ec ); BOOST_CHECK_EQUAL( std::string(buf,10), "aaaaaxxxxx" ); BOOST_CHECK_EQUAL( con->indef_read_total, 5 ); - + // A subsequent read should read 5 more because the indef read refreshes // itself. The new read will start again at the beginning of the buffer. BOOST_CHECK_EQUAL(con->read_some(input+5,5), 5); @@ -539,7 +573,7 @@ websocketpp::lib::error_code sd_handler(websocketpp::connection_hdl) { BOOST_AUTO_TEST_CASE( shutdown_handler ) { stub_con::ptr con(new stub_con(true,alogger,elogger)); - + con->set_shutdown_handler(&sd_handler); BOOST_CHECK_EQUAL( con->ec, make_error_code(websocketpp::error::test) ); con->shutdown(); diff --git a/tutorials/utility_client/utility_client.md b/tutorials/utility_client/utility_client.md index 5490f571b..85cc92817 100644 --- a/tutorials/utility_client/utility_client.md +++ b/tutorials/utility_client/utility_client.md @@ -235,7 +235,7 @@ The `websocket_endpoint` object has gained some new data members and methods. It A new WebSocket connection is initiated via a three step process. First, a connection request is created by `endpoint::get_connection(uri)`. Next, the connection request is configured. Lastly, the connection request is submitted back to the endpoint via `endpoint::connect()` which adds it to the queue of new connections to make. > ###### Terminology `connection_ptr` -> WebSocket++ keeps track of connection related resources using a reference counted shared pointer. The type of this pointer is `endpoint::connection_ptr`. A `connection_ptr` allows direct access to information about the connection and allows changing connection settings. Because of this direct access and their internal resource management role within the library it is not safe to for end applications to use `connection_ptr` except in the specific circumstances detailed below. +> WebSocket++ keeps track of connection related resources using a reference counted shared pointer. The type of this pointer is `endpoint::connection_ptr`. A `connection_ptr` allows direct access to information about the connection and allows changing connection settings. Because of this direct access and their internal resource management role within the library it is not safe for end applications to use `connection_ptr` except in the specific circumstances detailed below. > > **When is it safe to use `connection_ptr`?** > - After `endpoint::get_connection(...)` and before `endpoint::connect()`: `get_connection` returns a `connection_ptr`. It is safe to use this pointer to configure your new connection. Once you submit the connection to `connect` you may no longer use the `connection_ptr` and should discard it immediately for optimal memory management. @@ -813,6 +813,13 @@ Enter Command: show 0 _Using TLS / Secure WebSockets_ +- Change the includes +- link to the new library dependencies +- Switch the config +- add the `tls_init_handler` +- configure the SSL context for desired security level +- mixing secure and non-secure connections in one application. + Chapter 2: Intermediate Features -------------------------------- diff --git a/websocketpp/common/asio.hpp b/websocketpp/common/asio.hpp new file mode 100644 index 000000000..ca4835939 --- /dev/null +++ b/websocketpp/common/asio.hpp @@ -0,0 +1,131 @@ +/* + * 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. + * + */ + +#ifndef WEBSOCKETPP_COMMON_ASIO_HPP +#define WEBSOCKETPP_COMMON_ASIO_HPP + +// This file goes to some length to preserve compatibility with versions of +// boost older than 1.49 (where the first modern steady_timer timer based on +// boost/std chrono was introduced. +// +// For the versions older than 1.49, the deadline_timer is used instead. this +// brings in dependencies on boost date_time and it has a different interface +// that is normalized by the `lib::asio::is_neg` and `lib::asio::milliseconds` +// wrappers provided by this file. +// +// The primary reason for this continued support is that boost 1.48 is the +// default and not easily changeable version of boost supplied by the package +// manager of popular Linux distributions like Ubuntu 12.04 LTS. Once the need +// for this has passed this should be cleaned up and simplified. + +#ifdef ASIO_STANDALONE + #include + + #if (ASIO_VERSION/100000) == 1 && ((ASIO_VERSION/100)%1000) < 8 + static_assert(false, "The minimum version of standalone Asio is 1.8.0"); + #endif + + #include + #include + #include +#else + #include + + // See note above about boost <1.49 compatibility. If we are running on + // boost > 1.48 pull in the steady timer and chrono library + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48 + #include + #include + #endif + + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef ASIO_STANDALONE + namespace asio { + using namespace ::asio; + // Here we assume that we will be using std::error_code with standalone + // Asio. This is probably a good assumption, but it is possible in rare + // cases that local Asio versions would be used. + using std::errc; + + // See note above about boost <1.49 compatibility. Because we require + // a standalone Asio version of 1.8+ we are guaranteed to have + // steady_timer available. By convention we require the chrono library + // (either boost or std) for use with standalone Asio. + template + bool is_neg(T duration) { + return duration.count() < 0; + } + inline lib::chrono::milliseconds milliseconds(long duration) { + return lib::chrono::milliseconds(duration); + } + } // namespace asio + +#else + namespace asio { + using namespace boost::asio; + + // See note above about boost <1.49 compatibility + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48 + // Using boost::asio >=1.49 so we use chrono and steady_timer + template + bool is_neg(T duration) { + return duration.count() < 0; + } + inline lib::chrono::milliseconds milliseconds(long duration) { + return lib::chrono::milliseconds(duration); + } + #else + // Using boost::asio <1.49 we pretend a deadline timer is a steady + // timer and wrap the negative detection and duration conversion + // appropriately. + typedef boost::asio::deadline_timer steady_timer; + + template + bool is_neg(T duration) { + return duration.is_negative(); + } + inline boost::posix_time::time_duration milliseconds(long duration) { + return boost::posix_time::milliseconds(duration); + } + #endif + + using boost::system::error_code; + namespace errc = boost::system::errc; + } // namespace asio +#endif + + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_ASIO_HPP diff --git a/websocketpp/common/asio_ssl.hpp b/websocketpp/common/asio_ssl.hpp new file mode 100644 index 000000000..cc74297ea --- /dev/null +++ b/websocketpp/common/asio_ssl.hpp @@ -0,0 +1,37 @@ +/* + * 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. + * + */ + +#ifndef WEBSOCKETPP_COMMON_ASIO_SSL_HPP +#define WEBSOCKETPP_COMMON_ASIO_SSL_HPP + +#ifdef ASIO_STANDALONE + #include +#else + #include +#endif + +#endif // WEBSOCKETPP_COMMON_ASIO_SSL_HPP diff --git a/websocketpp/common/chrono.hpp b/websocketpp/common/chrono.hpp index c536a3652..d50dd1209 100644 --- a/websocketpp/common/chrono.hpp +++ b/websocketpp/common/chrono.hpp @@ -28,7 +28,12 @@ #ifndef WEBSOCKETPP_COMMON_CHRONO_HPP #define WEBSOCKETPP_COMMON_CHRONO_HPP -#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_ +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_ #ifndef _WEBSOCKETPP_CPP11_CHRONO_ #define _WEBSOCKETPP_CPP11_CHRONO_ #endif @@ -44,9 +49,9 @@ namespace websocketpp { namespace lib { #ifdef _WEBSOCKETPP_CPP11_CHRONO_ - using std::chrono::system_clock; + namespace chrono = std::chrono; #else - using boost::chrono::system_clock; + namespace chrono = boost::chrono; #endif } // namespace lib diff --git a/websocketpp/common/cpp11.hpp b/websocketpp/common/cpp11.hpp index 6c79d644d..a150de363 100644 --- a/websocketpp/common/cpp11.hpp +++ b/websocketpp/common/cpp11.hpp @@ -80,6 +80,12 @@ #ifndef _WEBSOCKETPP_NULLPTR_TOKEN_ #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr #endif + #ifndef _WEBSOCKETPP_MOVE_SEMANTICS_ + #define _WEBSOCKETPP_MOVE_SEMANTICS_ + #endif + #ifndef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + #define _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + #endif #ifndef __GNUC__ // GCC as of version 4.9 (latest) does not support std::put_time yet. diff --git a/websocketpp/common/network.hpp b/websocketpp/common/network.hpp index 3f9839665..26a4cb9ea 100644 --- a/websocketpp/common/network.hpp +++ b/websocketpp/common/network.hpp @@ -36,6 +36,8 @@ #include #endif +#include + namespace websocketpp { namespace lib { namespace net { diff --git a/websocketpp/common/system_error.hpp b/websocketpp/common/system_error.hpp index 4abe1732f..e5aea2549 100644 --- a/websocketpp/common/system_error.hpp +++ b/websocketpp/common/system_error.hpp @@ -61,6 +61,7 @@ namespace websocketpp { namespace lib { #ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + using std::errc; using std::error_code; using std::error_category; using std::error_condition; @@ -68,6 +69,7 @@ namespace lib { #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace std { #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ } #else + namespace errc = boost::system::errc; using boost::system::error_code; using boost::system::error_category; using boost::system::error_condition; diff --git a/websocketpp/common/type_traits.hpp b/websocketpp/common/type_traits.hpp new file mode 100644 index 000000000..72cc2e1e4 --- /dev/null +++ b/websocketpp/common/type_traits.hpp @@ -0,0 +1,63 @@ +/* + * 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. + * + */ + +#ifndef WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP +#define WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_TYPE_TRAITS_ + #ifndef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #define _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #endif +#endif + + +#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #include +#else + #include +#endif + + + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + using std::aligned_storage; +#else + using boost::aligned_storage; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 954683279..203de6f66 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -214,6 +214,20 @@ namespace internal_state { PROCESS_CONNECTION = 7 }; } // namespace internal_state + + +namespace http_state { + // states to keep track of the progress of http connections + + enum value { + init = 0, + deferred = 1, + headers_written = 2, + body_written = 3, + closed = 4 + }; +} // namespace http_state + } // namespace session /// Represents an individual WebSocket connection @@ -312,6 +326,7 @@ class connection , m_local_close_code(close::status::abnormal_close) , m_remote_close_code(close::status::abnormal_close) , m_is_http(false) + , m_http_state(session::http_state::init) , m_was_clean(false) { m_alog.write(log::alevel::devel,"connection constructor"); @@ -1060,6 +1075,52 @@ class connection request_type const & get_request() const { return m_request; } + + /// Defer HTTP Response until later (Exception free) + /** + * Used in the http handler to defer the HTTP response for this connection + * until later. Handshake timers will be canceled and the connection will be + * left open until `send_http_response` or an equivalent is called. + * + * Warning: deferred connections won't time out and as a result can tie up + * resources. + * + * @since 0.6.0 + * + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code defer_http_response(); + + /// Send deferred HTTP Response (exception free) + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * @since 0.6.0 + * + * @param ec A status code, zero on success, non-zero otherwise + */ + void send_http_response(lib::error_code & ec); + + /// Send deferred HTTP Response + void send_http_response(); + + // TODO HTTPNBIO: write_headers + // function that processes headers + status so far and writes it to the wire + // beginning the HTTP response body state. This method will ignore anything + // in the response body. + + // TODO HTTPNBIO: write_body_message + // queues the specified message_buffer for async writing + + // TODO HTTPNBIO: finish connection + // + + // TODO HTTPNBIO: write_response + // Writes the whole response, headers + body and closes the connection + + ///////////////////////////////////////////////////////////// // Pass-through access to the other connection information // @@ -1202,7 +1263,8 @@ class connection void handle_read_http_response(lib::error_code const & ec, size_t bytes_transferred); - void handle_send_http_response(lib::error_code const & ec); + + void handle_write_http_response(lib::error_code const & ec); void handle_send_http_request(lib::error_code const & ec); void handle_open_handshake_timeout(lib::error_code const & ec); @@ -1254,13 +1316,13 @@ class connection lib::error_code process_handshake_request(); private: /// Completes m_response, serializes it, and sends it out on the wire. - void send_http_response(lib::error_code const & ec); + void write_http_response(lib::error_code const & ec); /// Sends an opening WebSocket connect request void send_http_request(); - /// Alternate path for send_http_response in error conditions - void send_http_response_error(lib::error_code const & ec); + /// Alternate path for write_http_response in error conditions + void write_http_response_error(lib::error_code const & ec); /// Process control message /** @@ -1510,6 +1572,10 @@ class connection /// A flag that gets set once it is determined that the connection is an /// HTTP connection and not a WebSocket one. bool m_is_http; + + /// A flag that gets set when the completion of an http connection is + /// deferred until later. + session::http_state::value m_http_state; bool m_was_clean; diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index 15db1ccb3..908a6f96f 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -104,6 +104,56 @@ class endpoint : public config::transport_type, public config::endpoint_base { transport_type::init_logging(&m_alog, &m_elog); } + + /// Destructor + ~endpoint() {} + + #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy constructor because endpoints are not copyable + endpoint(endpoint &) = delete; + + // no copy assignment operator because endpoints are not copyable + endpoint & operator=(endpoint const &) = delete; + #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + + #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + endpoint(endpoint && o) + : config::transport_type(std::move(o)) + , config::endpoint_base(std::move(o)) + , m_alog(std::move(o.m_alog)) + , m_elog(std::move(o.m_elog)) + , m_user_agent(std::move(o.m_user_agent)) + , m_open_handler(std::move(o.m_open_handler)) + + , m_close_handler(std::move(o.m_close_handler)) + , m_fail_handler(std::move(o.m_fail_handler)) + , m_ping_handler(std::move(o.m_ping_handler)) + , m_pong_handler(std::move(o.m_pong_handler)) + , m_pong_timeout_handler(std::move(o.m_pong_timeout_handler)) + , m_interrupt_handler(std::move(o.m_interrupt_handler)) + , m_http_handler(std::move(o.m_http_handler)) + , m_validate_handler(std::move(o.m_validate_handler)) + , m_message_handler(std::move(o.m_message_handler)) + + , m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur) + , m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur) + , m_pong_timeout_dur(o.m_pong_timeout_dur) + , m_max_message_size(o.m_max_message_size) + , m_max_http_body_size(o.m_max_http_body_size) + + , m_rng(std::move(o.m_rng)) + , m_is_server(o.m_is_server) + {} + + #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + endpoint & operator=(endpoint &&) = delete; + #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + + #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + /// Returns the user agent string that this endpoint will use /** * Returns the user agent string that this endpoint will use when creating @@ -466,6 +516,35 @@ class endpoint : public config::transport_type, public config::endpoint_base { /// Resume reading of new data void resume_reading(connection_hdl hdl); + /// Send deferred HTTP Response + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * Exception free variant + * + * @since 0.6.0 + * + * @param hdl The connection to send the response on + * @param ec A status code, zero on success, non-zero otherwise + */ + void send_http_response(connection_hdl hdl, lib::error_code & ec); + + /// Send deferred HTTP Response (exception free) + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * Exception variant + * + * @since 0.6.0 + * + * @param hdl The connection to send the response on + */ + void send_http_response(connection_hdl hdl); + /// Create a message and add it to the outgoing send queue (exception free) /** * Convenience method to send a message given a payload string and an opcode @@ -559,7 +638,6 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @return the connection_ptr. May be NULL if the handle was invalid. */ connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) { - scoped_lock_type lock(m_mutex); connection_ptr con = lib::static_pointer_cast( hdl.lock()); if (!con) { diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp index c9327201d..562fb6ed7 100644 --- a/websocketpp/error.hpp +++ b/websocketpp/error.hpp @@ -255,7 +255,7 @@ class exception : public std::exception { {} explicit exception(lib::error_code ec) - : m_code(ec) + : m_msg(ec.message()), m_code(ec) {} ~exception() throw() {} @@ -268,7 +268,7 @@ class exception : public std::exception { return m_code; } - std::string m_msg; + const std::string m_msg; lib::error_code m_code; }; diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 5648a5517..b191756d7 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -636,7 +636,66 @@ void connection::remove_header(std::string const & key) } } +/// Defer HTTP Response until later +/** + * Used in the http handler to defer the HTTP response for this connection + * until later. Handshake timers will be canceled and the connection will be + * left open until `send_http_response` or an equivalent is called. + * + * Warning: deferred connections won't time out and as a result can tie up + * resources. + * + * @return A status code, zero on success, non-zero otherwise + */ +template +lib::error_code connection::defer_http_response() { + // Cancel handshake timer, otherwise the connection will time out and we'll + // close the connection before the app has a chance to send a response. + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + // Do something to signal deferral + m_http_state = session::http_state::deferred; + + return lib::error_code(); +} + +/// Send deferred HTTP Response (exception free) +/** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * @since 0.6.0 + * + * @param ec A status code, zero on success, non-zero otherwise + */ +template +void connection::send_http_response(lib::error_code & ec) { + { + scoped_lock_type lock(m_connection_state_lock); + if (m_http_state != session::http_state::deferred) { + ec = error::make_error_code(error::invalid_state); + return; + } + + m_http_state = session::http_state::body_written; + } + + this->write_http_response(lib::error_code()); + ec = lib::error_code(); +} +template +void connection::send_http_response() { + lib::error_code ec; + this->send_http_response(ec); + if (ec) { + throw exception(ec); + } +} @@ -729,7 +788,7 @@ void connection::read_handshake(size_t num_bytes) { ); } -// All exit paths for this function need to call send_http_response() or submit +// All exit paths for this function need to call write_http_response() or submit // a new read request with this function as the handler. template void connection::handle_read_handshake(lib::error_code const & ec, @@ -785,7 +844,7 @@ void connection::handle_read_handshake(lib::error_code const & ec, // All HTTP exceptions will result in this request failing and an error // response being returned. No more bytes will be read in this con. m_response.set_status(e.m_error_code,e.m_error_msg); - this->send_http_response_error(error::make_error_code(error::http_parse_error)); + this->write_http_response_error(error::make_error_code(error::http_parse_error)); return; } @@ -807,7 +866,7 @@ void connection::handle_read_handshake(lib::error_code const & ec, if (m_request.ready()) { lib::error_code processor_ec = this->initialize_processor(); if (processor_ec) { - this->send_http_response_error(processor_ec); + this->write_http_response_error(processor_ec); return; } @@ -824,7 +883,7 @@ void connection::handle_read_handshake(lib::error_code const & ec, // TODO: need more bytes m_alog.write(log::alevel::devel,"short key3 read"); m_response.set_status(http::status_code::internal_server_error); - this->send_http_response_error(processor::error::make_error_code(processor::error::short_key3)); + this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3)); return; } } @@ -848,7 +907,9 @@ void connection::handle_read_handshake(lib::error_code const & ec, // We have the complete request. Process it. lib::error_code handshake_ec = this->process_handshake_request(); - this->send_http_response(handshake_ec); + if (!m_is_http || m_http_state != session::http_state::deferred) { + this->write_http_response(handshake_ec); + } } else { // read at least 1 more byte transport_con_type::async_read_at_least( @@ -865,26 +926,26 @@ void connection::handle_read_handshake(lib::error_code const & ec, } } -// send_http_response requires the request to be fully read and the connection +// write_http_response requires the request to be fully read and the connection // to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors // before the request is fully read (specifically at a point where we aren't // sure if the hybi00 key3 bytes need to be read). This method sets the correct -// state and calls send_http_response +// state and calls write_http_response template -void connection::send_http_response_error(lib::error_code const & ec) { +void connection::write_http_response_error(lib::error_code const & ec) { if (m_internal_state != istate::READ_HTTP_REQUEST) { m_alog.write(log::alevel::devel, - "send_http_response_error called in invalid state"); + "write_http_response_error called in invalid state"); this->terminate(error::make_error_code(error::invalid_state)); return; } m_internal_state = istate::PROCESS_HTTP_REQUEST; - this->send_http_response(ec); + this->write_http_response(ec); } -// All exit paths for this function need to call send_http_response() or submit +// All exit paths for this function need to call write_http_response() or submit // a new read request with this function as the handler. template void connection::handle_read_frame(lib::error_code const & ec, @@ -1120,6 +1181,7 @@ lib::error_code connection::process_handshake_request() { if (m_http_handler) { m_is_http = true; m_http_handler(m_connection_hdl); + if (m_state == session::state::closed) { return error::make_error_code(error::http_connection_ended); } @@ -1214,8 +1276,8 @@ lib::error_code connection::process_handshake_request() { } template -void connection::send_http_response(lib::error_code const & ec) { - m_alog.write(log::alevel::devel,"connection send_http_response"); +void connection::write_http_response(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"connection write_http_response"); if (ec == error::make_error_code(error::http_connection_ended)) { m_alog.write(log::alevel::http,"An HTTP handler took over the connection."); @@ -1261,7 +1323,7 @@ void connection::send_http_response(lib::error_code const & ec) { m_handshake_buffer.data(), m_handshake_buffer.size(), lib::bind( - &type::handle_send_http_response, + &type::handle_write_http_response, type::get_shared(), lib::placeholders::_1 ) @@ -1269,8 +1331,8 @@ void connection::send_http_response(lib::error_code const & ec) { } template -void connection::handle_send_http_response(lib::error_code const & ec) { - m_alog.write(log::alevel::devel,"handle_send_http_response"); +void connection::handle_write_http_response(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"handle_write_http_response"); lib::error_code ecm = ec; @@ -1286,7 +1348,7 @@ void connection::handle_send_http_response(lib::error_code const & ec) { // usually by the handshake timer. This is basically expected // (though hopefully rare) and there is nothing we can do so ignore. m_alog.write(log::alevel::devel, - "handle_send_http_response invoked after connection was closed"); + "handle_write_http_response invoked after connection was closed"); return; } else { ecm = error::make_error_code(error::invalid_state); @@ -1301,7 +1363,7 @@ void connection::handle_send_http_response(lib::error_code const & ec) { return; } - log_err(log::elevel::rerror,"handle_send_http_response",ecm); + log_err(log::elevel::rerror,"handle_write_http_response",ecm); this->terminate(ecm); return; } @@ -1634,7 +1696,10 @@ void connection::terminate(lib::error_code const & ec) { m_local_close_reason = ec.message(); } - // TODO: does this need a mutex? + // TODO: does any of this need a mutex? + if (m_is_http) { + m_http_state = session::http_state::closed; + } if (m_state == session::state::connecting) { m_state = session::state::closed; tstat = failed; diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp index 5895199a0..e09cda95e 100644 --- a/websocketpp/impl/endpoint_impl.hpp +++ b/websocketpp/impl/endpoint_impl.hpp @@ -142,7 +142,21 @@ void endpoint::resume_reading(connection_hdl hdl) { if (ec) { throw exception(ec); } } +template +void endpoint::send_http_response(connection_hdl hdl, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->send_http_response(ec); +} +template +void endpoint::send_http_response(connection_hdl hdl) { + lib::error_code ec; + send_http_response(hdl,ec); + if (ec) { throw exception(ec); } +} template void endpoint::send(connection_hdl hdl, std::string const & payload, diff --git a/websocketpp/logger/basic.hpp b/websocketpp/logger/basic.hpp index 95099a834..84514130e 100644 --- a/websocketpp/logger/basic.hpp +++ b/websocketpp/logger/basic.hpp @@ -80,6 +80,36 @@ class basic { , m_dynamic_channels(0) , m_out(out) {} + /// Destructor + ~basic() {} + + /// Copy constructor + basic(basic const & other) + : m_static_channels(other.m_static_channels) + , m_dynamic_channels(other.m_dynamic_channels) + , m_out(other.m_out) + {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy assignment operator because of const member variables + basic & operator=(basic const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + basic(basic && other) + : m_static_channels(other.m_static_channels) + , m_dynamic_channels(other.m_dynamic_channels) + , m_out(other.m_out) + {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + basic & operator=(basic &&) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + void set_ostream(std::ostream * out = &std::cout) { m_out = out; } diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp index 7238377a4..b46593f65 100644 --- a/websocketpp/processors/hybi13.hpp +++ b/websocketpp/processors/hybi13.hpp @@ -1018,7 +1018,8 @@ class hybi13 : public processor { out->set_header(frame::prepare_header(h,e)); std::copy(payload.begin(),payload.end(),o.begin()); } - + + out->set_opcode(op); out->set_prepared(true); return lib::error_code(); diff --git a/websocketpp/roles/client_endpoint.hpp b/websocketpp/roles/client_endpoint.hpp index d5a9f0038..b793873ac 100644 --- a/websocketpp/roles/client_endpoint.hpp +++ b/websocketpp/roles/client_endpoint.hpp @@ -29,6 +29,7 @@ #define WEBSOCKETPP_CLIENT_ENDPOINT_HPP #include +#include #include diff --git a/websocketpp/roles/server_endpoint.hpp b/websocketpp/roles/server_endpoint.hpp index 45a4bef9e..dadcf72f7 100644 --- a/websocketpp/roles/server_endpoint.hpp +++ b/websocketpp/roles/server_endpoint.hpp @@ -69,6 +69,28 @@ class server : public endpoint,config> { endpoint_type::m_alog.write(log::alevel::devel, "server constructor"); } + /// Destructor + ~server() {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy constructor because endpoints are not copyable + server(server &) = delete; + + // no copy assignment operator because endpoints are not copyable + server & operator=(server const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + server(server && o) : endpoint,config>(std::move(o)) {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + server & operator=(server &&) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Create and initialize a new connection /** * The connection will be initialized and ready to begin. Call its start() diff --git a/websocketpp/transport/asio/base.hpp b/websocketpp/transport/asio/base.hpp index 8ede3aaab..6ee881ab8 100644 --- a/websocketpp/transport/asio/base.hpp +++ b/websocketpp/transport/asio/base.hpp @@ -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: @@ -28,123 +28,107 @@ #ifndef WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP #define WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP +#include #include #include #include - -#include - -#include -#include -#include +#include #include namespace websocketpp { namespace transport { -/// Transport policy that uses boost::asio +/// Transport policy that uses asio /** - * This policy uses a single boost::asio io_service to provide transport + * This policy uses a single asio io_service to provide transport * services to a WebSocket++ endpoint. */ namespace asio { -// - // Class to manage the memory to be used for handler-based custom allocation. // It contains a single block of memory which may be returned for allocation // requests. If the memory is in use when an allocation request is made, the // allocator delegates allocation to the global heap. -class handler_allocator - : private boost::noncopyable -{ +class handler_allocator { public: - handler_allocator() - : in_use_(false) - { - } - - void* allocate(std::size_t size) - { - if (!in_use_ && size < storage_.size) - { - in_use_ = true; - return storage_.address(); - } - else - { - return ::operator new(size); + static const size_t size = 1024; + + handler_allocator() : m_in_use(false) {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + handler_allocator(handler_allocator const & cpy) = delete; + handler_allocator & operator =(handler_allocator const &) = delete; +#endif + + void * allocate(std::size_t memsize) { + if (!m_in_use && memsize < size) { + m_in_use = true; + return static_cast(&m_storage); + } else { + return ::operator new(memsize); + } } - } - void deallocate(void* pointer) - { - if (pointer == storage_.address()) - { - in_use_ = false; - } - else - { - ::operator delete(pointer); + void deallocate(void * pointer) { + if (pointer == &m_storage) { + m_in_use = false; + } else { + ::operator delete(pointer); + } } - } private: - // Storage space used for handler-based custom memory allocation. - boost::aligned_storage<1024> storage_; + // Storage space used for handler-based custom memory allocation. + lib::aligned_storage::type m_storage; - // Whether the handler-based custom allocation storage has been used. - bool in_use_; + // Whether the handler-based custom allocation storage has been used. + bool m_in_use; }; // Wrapper class template for handler objects to allow handler memory // allocation to be customised. Calls to operator() are forwarded to the // encapsulated handler. template -class custom_alloc_handler -{ +class custom_alloc_handler { public: - custom_alloc_handler(handler_allocator& a, Handler h) - : allocator_(a), - handler_(h) - { - } - - template - void operator()(Arg1 arg1) - { - handler_(arg1); - } - - template - void operator()(Arg1 arg1, Arg2 arg2) - { - handler_(arg1, arg2); - } - - friend void* asio_handler_allocate(std::size_t size, - custom_alloc_handler* this_handler) - { - return this_handler->allocator_.allocate(size); - } - - friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/, - custom_alloc_handler* this_handler) - { - this_handler->allocator_.deallocate(pointer); - } + custom_alloc_handler(handler_allocator& a, Handler h) + : allocator_(a), + handler_(h) + {} + + template + void operator()(Arg1 arg1) { + handler_(arg1); + } + + template + void operator()(Arg1 arg1, Arg2 arg2) { + handler_(arg1, arg2); + } + + friend void* asio_handler_allocate(std::size_t size, + custom_alloc_handler * this_handler) + { + return this_handler->allocator_.allocate(size); + } + + friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/, + custom_alloc_handler * this_handler) + { + this_handler->allocator_.deallocate(pointer); + } private: - handler_allocator& allocator_; - Handler handler_; + handler_allocator & allocator_; + Handler handler_; }; // Helper function to wrap a handler object to add custom allocation. template inline custom_alloc_handler make_custom_alloc_handler( - handler_allocator& a, Handler h) + handler_allocator & a, Handler h) { - return custom_alloc_handler(a, h); + return custom_alloc_handler(a, h); } @@ -158,13 +142,13 @@ inline custom_alloc_handler make_custom_alloc_handler( template class endpoint; -typedef lib::function +typedef lib::function socket_shutdown_handler; -typedef lib::function async_read_handler; -typedef lib::function async_write_handler; typedef lib::function pre_init_handler; diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp index 0be40f6b4..635a0f3a1 100644 --- a/websocketpp/transport/asio/connection.hpp +++ b/websocketpp/transport/asio/connection.hpp @@ -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: @@ -37,15 +37,15 @@ #include #include +#include +#include +#include #include #include #include #include -#include -#include - #include #include #include @@ -57,10 +57,10 @@ namespace asio { typedef lib::function tcp_init_handler; -/// Boost Asio based connection transport component +/// Asio based connection transport component /** * transport::asio::connection implements a connection transport component using - * Boost ASIO that works with the transport::asio::endpoint endpoint transport + * Asio that works with the transport::asio::endpoint endpoint transport * component. */ template @@ -85,12 +85,12 @@ class connection : public config::socket_type::socket_con_type { typedef typename config::response_type response_type; typedef typename response_type::ptr response_ptr; - /// 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 io_service::strand being used - typedef lib::shared_ptr strand_ptr; - /// Type of a pointer to the ASIO timer class - typedef lib::shared_ptr timer_ptr; + /// Type of a pointer to the Asio io_service being used + typedef lib::asio::io_service * io_service_ptr; + /// Type of a pointer to the Asio io_service::strand being used + typedef lib::shared_ptr strand_ptr; + /// Type of a pointer to the Asio timer class + typedef lib::shared_ptr timer_ptr; // connection is friends with its associated endpoint to allow the endpoint // to call private/protected utility methods that we don't want to expose @@ -98,7 +98,7 @@ class connection : public config::socket_type::socket_con_type { friend class endpoint; // generate and manage our own io_service - explicit connection(bool is_server, alog_type& alog, elog_type& elog) + explicit connection(bool is_server, alog_type & alog, elog_type & elog) : m_is_server(is_server) , m_alog(alog) , m_elog(elog) @@ -115,6 +115,22 @@ class connection : public config::socket_type::socket_con_type { return socket_con_type::is_secure(); } + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * This transport policy doesn't use the uri except to forward it to the + * socket layer. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr u) { + socket_con_type::set_uri(u); + } + /// Sets the tcp pre init handler /** * The tcp pre init handler is called after the raw tcp connection has been @@ -295,9 +311,9 @@ class connection : public config::socket_type::socket_con_type { * needed. */ timer_ptr set_timer(long duration, timer_handler callback) { - timer_ptr new_timer = lib::make_shared( + timer_ptr new_timer = lib::make_shared( lib::ref(*m_io_service), - boost::posix_time::milliseconds(duration) + lib::asio::milliseconds(duration) ); if (config::enable_multithreading) { @@ -331,10 +347,10 @@ class connection : public config::socket_type::socket_con_type { * @param ec The status code */ void handle_timer(timer_ptr, timer_handler callback, - boost::system::error_code const & ec) + lib::asio::error_code const & ec) { if (ec) { - if (ec == boost::asio::error::operation_aborted) { + if (ec == lib::asio::error::operation_aborted) { callback(make_error_code(transport::error::operation_aborted)); } else { log_err(log::elevel::info,"asio handle_timer",ec); @@ -344,7 +360,7 @@ class connection : public config::socket_type::socket_con_type { callback(lib::error_code()); } } -protected: + /// Get a pointer to this connection's strand strand_ptr get_strand() { return m_strand; @@ -353,7 +369,7 @@ class connection : public config::socket_type::socket_con_type { /// Initialize transport for reading /** * init_asio is called once immediately after construction to initialize - * boost::asio components to the io_service + * Asio components to the io_service * * The transport initialization sequence consists of the following steps: * - Pre-init: the underlying socket is initialized to the point where @@ -367,6 +383,7 @@ class connection : public config::socket_type::socket_con_type { * read or write the WebSocket handshakes. At this point the original * callback function is called. */ +protected: void init(init_handler callback) { if (m_alog.static_test(log::alevel::devel)) { m_alog.write(log::alevel::devel,"asio connection init"); @@ -411,7 +428,7 @@ class connection : public config::socket_type::socket_con_type { /// Finish constructing the transport /** * init_asio is called once immediately after construction to initialize - * boost::asio components to the io_service. + * Asio components to the io_service. * * @param io_service A pointer to the io_service to register with this * connection @@ -422,7 +439,7 @@ class connection : public config::socket_type::socket_con_type { m_io_service = io_service; if (config::enable_multithreading) { - m_strand = lib::make_shared( + m_strand = lib::make_shared( lib::ref(*io_service)); m_async_read_handler = m_strand->wrap(lib::bind( @@ -555,7 +572,7 @@ class connection : public config::socket_type::socket_con_type { lib::error_code const & ec) { if (ec == transport::error::operation_aborted || - (post_timer && post_timer->expires_from_now().is_negative())) + (post_timer && lib::asio::is_neg(post_timer->expires_from_now()))) { m_alog.write(log::alevel::devel,"post_init cancelled"); return; @@ -590,8 +607,8 @@ class connection : public config::socket_type::socket_con_type { m_proxy_data->write_buf = m_proxy_data->req.raw(); - m_bufs.push_back(boost::asio::buffer(m_proxy_data->write_buf.data(), - m_proxy_data->write_buf.size())); + m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(), + m_proxy_data->write_buf.size())); m_alog.write(log::alevel::devel,m_proxy_data->write_buf); @@ -608,7 +625,7 @@ class connection : public config::socket_type::socket_con_type { // Send proxy request if (config::enable_multithreading) { - boost::asio::async_write( + lib::asio::async_write( socket_con_type::get_next_layer(), m_bufs, m_strand->wrap(lib::bind( @@ -618,7 +635,7 @@ class connection : public config::socket_type::socket_con_type { )) ); } else { - boost::asio::async_write( + lib::asio::async_write( socket_con_type::get_next_layer(), m_bufs, lib::bind( @@ -648,7 +665,7 @@ class connection : public config::socket_type::socket_con_type { } void handle_proxy_write(init_handler callback, - boost::system::error_code const & ec) + lib::asio::error_code const & ec) { if (m_alog.static_test(log::alevel::devel)) { m_alog.write(log::alevel::devel, @@ -660,8 +677,8 @@ class connection : public config::socket_type::socket_con_type { // Timer expired or the operation was aborted for some reason. // Whatever aborted it will be issuing the callback so we are safe to // return - if (ec == boost::asio::error::operation_aborted || - m_proxy_data->timer->expires_from_now().is_negative()) + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) { m_elog.write(log::elevel::devel,"write operation aborted"); return; @@ -691,7 +708,7 @@ class connection : public config::socket_type::socket_con_type { } if (config::enable_multithreading) { - boost::asio::async_read_until( + lib::asio::async_read_until( socket_con_type::get_next_layer(), m_proxy_data->read_buf, "\r\n\r\n", @@ -702,7 +719,7 @@ class connection : public config::socket_type::socket_con_type { )) ); } else { - boost::asio::async_read_until( + lib::asio::async_read_until( socket_con_type::get_next_layer(), m_proxy_data->read_buf, "\r\n\r\n", @@ -722,7 +739,7 @@ class connection : public config::socket_type::socket_con_type { * @param bytes_transferred The number of bytes read */ void handle_proxy_read(init_handler callback, - boost::system::error_code const & ec, size_t) + lib::asio::error_code const & ec, size_t) { if (m_alog.static_test(log::alevel::devel)) { m_alog.write(log::alevel::devel, @@ -732,8 +749,8 @@ class connection : public config::socket_type::socket_con_type { // Timer expired or the operation was aborted for some reason. // Whatever aborted it will be issuing the callback so we are safe to // return - if (ec == boost::asio::error::operation_aborted || - m_proxy_data->timer->expires_from_now().is_negative()) + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) { m_elog.write(log::elevel::devel,"read operation aborted"); return; @@ -836,10 +853,10 @@ class connection : public config::socket_type::socket_con_type { "asio con async_read_at_least called with bad handler"); } - boost::asio::async_read( + lib::asio::async_read( socket_con_type::get_socket(), - boost::asio::buffer(buf,len), - boost::asio::transfer_at_least(num_bytes), + lib::asio::buffer(buf,len), + lib::asio::transfer_at_least(num_bytes), make_custom_alloc_handler( m_read_handler_allocator, m_async_read_handler @@ -847,14 +864,14 @@ class connection : public config::socket_type::socket_con_type { ); } - void handle_async_read(boost::system::error_code const & ec, + void handle_async_read(lib::asio::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 + // translate asio error codes into more lib::error_codes lib::error_code tec; - if (ec == boost::asio::error::eof) { + if (ec == lib::asio::error::eof) { tec = make_error_code(transport::error::eof); } else if (ec) { // We don't know much more about the error at this point. As our @@ -890,11 +907,11 @@ class connection : public config::socket_type::socket_con_type { return; } - m_bufs.push_back(boost::asio::buffer(buf,len)); + m_bufs.push_back(lib::asio::buffer(buf,len)); m_write_handler = handler; - boost::asio::async_write( + lib::asio::async_write( socket_con_type::get_socket(), m_bufs, make_custom_alloc_handler( @@ -914,12 +931,12 @@ class connection : public config::socket_type::socket_con_type { std::vector::const_iterator it; for (it = bufs.begin(); it != bufs.end(); ++it) { - m_bufs.push_back(boost::asio::buffer((*it).buf,(*it).len)); + m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len)); } m_write_handler = handler; - boost::asio::async_write( + lib::asio::async_write( socket_con_type::get_socket(), m_bufs, make_custom_alloc_handler( @@ -934,7 +951,7 @@ class connection : public config::socket_type::socket_con_type { * @param ec The status code * @param bytes_transferred The number of bytes read */ - void handle_async_write(boost::system::error_code const & ec, size_t) { + void handle_async_write(lib::asio::error_code const & ec, size_t) { m_bufs.clear(); lib::error_code tec; if (ec) { @@ -1061,10 +1078,10 @@ class connection : public config::socket_type::socket_con_type { } void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler - callback, boost::system::error_code const & ec) + callback, lib::asio::error_code const & ec) { - if (ec == boost::asio::error::operation_aborted || - shutdown_timer->expires_from_now().is_negative()) + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(shutdown_timer->expires_from_now())) { m_alog.write(log::alevel::devel,"async_shutdown cancelled"); return; @@ -1074,7 +1091,7 @@ class connection : public config::socket_type::socket_con_type { lib::error_code tec; if (ec) { - if (ec == boost::asio::error::not_connected) { + if (ec == lib::asio::error::not_connected) { // The socket was already closed when we tried to close it. This // happens periodically (usually if a read or write fails // earlier and if it is a real error will be caught at another @@ -1124,7 +1141,7 @@ class connection : public config::socket_type::socket_con_type { request_type req; response_type res; std::string write_buf; - boost::asio::streambuf read_buf; + lib::asio::streambuf read_buf; long timeout_proxy; timer_ptr timer; }; @@ -1137,7 +1154,7 @@ class connection : public config::socket_type::socket_con_type { strand_ptr m_strand; connection_hdl m_connection_hdl; - std::vector m_bufs; + std::vector m_bufs; // Handlers tcp_init_handler m_tcp_pre_init_handler; diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp index ef3b07db4..4e278d41c 100644 --- a/websocketpp/transport/asio/endpoint.hpp +++ b/websocketpp/transport/asio/endpoint.hpp @@ -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: @@ -37,10 +37,6 @@ #include -#include -#include -#include - #include #include @@ -48,10 +44,10 @@ namespace websocketpp { namespace transport { namespace asio { -/// Boost Asio based endpoint transport component +/// Asio based endpoint transport component /** * transport::asio::endpoint implements an endpoint transport component using - * Boost ASIO. + * Asio. */ template class endpoint : public config::socket_type { @@ -81,15 +77,15 @@ class endpoint : public config::socket_type { 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; + typedef lib::asio::io_service * io_service_ptr; /// Type of a shared pointer to the acceptor being used - typedef lib::shared_ptr acceptor_ptr; + typedef lib::shared_ptr acceptor_ptr; /// Type of a shared pointer to the resolver being used - typedef lib::shared_ptr resolver_ptr; + typedef lib::shared_ptr resolver_ptr; /// Type of timer handle - typedef lib::shared_ptr timer_ptr; + typedef lib::shared_ptr timer_ptr; /// Type of a shared pointer to an io_service work object - typedef lib::shared_ptr work_ptr; + typedef lib::shared_ptr work_ptr; // generate and manage our own io_service explicit endpoint() @@ -113,23 +109,28 @@ class endpoint : public config::socket_type { /// transport::asio objects are moveable but not copyable or assignable. /// The following code sets this situation up based on whether or not we /// have C++11 support or not -#ifdef _WEBSOCKETPP_DELETED_FUNCTIONS_ - endpoint(const endpoint& src) = delete; +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + endpoint(const endpoint & src) = delete; endpoint& operator= (const endpoint & rhs) = delete; #else private: - endpoint(const endpoint& src); - endpoint& operator= (const endpoint & rhs); + endpoint(const endpoint & src); + endpoint & operator= (const endpoint & rhs); public: -#endif - -#ifdef _WEBSOCKETPP_RVALUE_REFERENCES_ - endpoint (endpoint&& src) - : m_io_service(src.m_io_service) +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + endpoint (endpoint && src) + : config::socket_type(std::move(src)) + , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler) + , m_tcp_post_init_handler(src.m_tcp_post_init_handler) + , m_io_service(src.m_io_service) , m_external_io_service(src.m_external_io_service) , m_acceptor(src.m_acceptor) - , m_listen_backlog(boost::asio::socket_base::max_connections) + , m_listen_backlog(lib::asio::socket_base::max_connections) , m_reuse_addr(src.m_reuse_addr) + , m_elog(src.m_elog) + , m_alog(src.m_alog) , m_state(src.m_state) { src.m_io_service = NULL; @@ -138,7 +139,7 @@ class endpoint : public config::socket_type { src.m_state = UNINITIALIZED; } - endpoint& operator= (const endpoint && rhs) { + /*endpoint & operator= (const endpoint && rhs) { if (this != &rhs) { m_io_service = rhs.m_io_service; m_external_io_service = rhs.m_external_io_service; @@ -150,12 +151,15 @@ class endpoint : public config::socket_type { rhs.m_io_service = NULL; rhs.m_external_io_service = false; rhs.m_acceptor = NULL; - rhs.m_listen_backlog = boost::asio::socket_base::max_connections; + rhs.m_listen_backlog = lib::asio::socket_base::max_connections; rhs.m_state = UNINITIALIZED; + + // TODO: this needs to be updated } return *this; - } -#endif + }*/ +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Return whether or not the endpoint produces secure connections. bool is_secure() const { return socket_type::is_secure(); @@ -183,7 +187,7 @@ class endpoint : public config::socket_type { m_io_service = ptr; m_external_io_service = true; - m_acceptor = lib::make_shared( + m_acceptor = lib::make_shared( lib::ref(*m_io_service)); m_state = READY; @@ -214,7 +218,7 @@ class endpoint : public config::socket_type { * @param ec Set to indicate what error occurred, if any. */ void init_asio(lib::error_code & ec) { - init_asio(new boost::asio::io_service(), ec); + init_asio(new lib::asio::io_service(), ec); m_external_io_service = false; } @@ -226,7 +230,7 @@ class endpoint : public config::socket_type { * @see init_asio(io_service_ptr ptr) */ void init_asio() { - init_asio(new boost::asio::io_service()); + init_asio(new lib::asio::io_service()); m_external_io_service = false; } @@ -325,7 +329,7 @@ class endpoint : public config::socket_type { * * @return A reference to the endpoint's io_service */ - boost::asio::io_service & get_io_service() { + lib::asio::io_service & get_io_service() { return *m_io_service; } @@ -337,7 +341,7 @@ class endpoint : public config::socket_type { * @param ep An endpoint to read settings from * @param ec Set to indicate what error occurred, if any. */ - void listen(boost::asio::ip::tcp::endpoint const & ep, lib::error_code & ec) + void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec) { if (m_state != READY) { m_elog->write(log::elevel::library, @@ -349,11 +353,11 @@ class endpoint : public config::socket_type { m_alog->write(log::alevel::devel,"asio::listen"); - boost::system::error_code bec; + lib::asio::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); + m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec); } if (!bec) { m_acceptor->bind(ep,bec); @@ -379,7 +383,7 @@ class endpoint : public config::socket_type { * * @param ep An endpoint to read settings from */ - void listen(boost::asio::ip::tcp::endpoint const & ep) { + void listen(lib::asio::ip::tcp::endpoint const & ep) { lib::error_code ec; listen(ep,ec); if (ec) { throw exception(ec); } @@ -392,8 +396,8 @@ class endpoint : public config::socket_type { * listening. * * Common options include: - * - IPv6 with mapped IPv4 for dual stack hosts boost::asio::ip::tcp::v6() - * - IPv4 only: boost::asio::ip::tcp::v4() + * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() + * - IPv4 only: lib::asio::ip::tcp::v4() * * @param internet_protocol The internet protocol to use. * @param port The port to listen on. @@ -403,7 +407,7 @@ class endpoint : public config::socket_type { void listen(InternetProtocol const & internet_protocol, uint16_t port, lib::error_code & ec) { - boost::asio::ip::tcp::endpoint ep(internet_protocol, port); + lib::asio::ip::tcp::endpoint ep(internet_protocol, port); listen(ep,ec); } @@ -414,8 +418,8 @@ class endpoint : public config::socket_type { * listening. * * Common options include: - * - IPv6 with mapped IPv4 for dual stack hosts boost::asio::ip::tcp::v6() - * - IPv4 only: boost::asio::ip::tcp::v4() + * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() + * - IPv4 only: lib::asio::ip::tcp::v4() * * @param internet_protocol The internet protocol to use. * @param port The port to listen on. @@ -423,7 +427,7 @@ class endpoint : public config::socket_type { template void listen(InternetProtocol const & internet_protocol, uint16_t port) { - boost::asio::ip::tcp::endpoint ep(internet_protocol, port); + lib::asio::ip::tcp::endpoint ep(internet_protocol, port); listen(ep); } @@ -440,7 +444,7 @@ class endpoint : public config::socket_type { * @param ec Set to indicate what error occurred, if any. */ void listen(uint16_t port, lib::error_code & ec) { - listen(boost::asio::ip::tcp::v6(), port, ec); + listen(lib::asio::ip::tcp::v6(), port, ec); } /// Set up endpoint for listening on a port @@ -456,13 +460,13 @@ class endpoint : public config::socket_type { * @param ec Set to indicate what error occurred, if any. */ void listen(uint16_t port) { - listen(boost::asio::ip::tcp::v6(), port); + listen(lib::asio::ip::tcp::v6(), port); } /// Set up endpoint for listening on a host and service (exception free) /** * Bind the internal acceptor using the given host and service. More details - * about what host and service can be are available in the boost asio + * about what host and service can be are available in the Asio * documentation for ip::basic_resolver_query::basic_resolver_query's * constructors. * @@ -478,7 +482,7 @@ class endpoint : public config::socket_type { void listen(std::string const & host, std::string const & service, lib::error_code & ec) { - using boost::asio::ip::tcp; + using lib::asio::ip::tcp; tcp::resolver r(*m_io_service); tcp::resolver::query query(host, service); tcp::resolver::iterator endpoint_iterator = r.resolve(query); @@ -495,7 +499,7 @@ class endpoint : public config::socket_type { /// Set up endpoint for listening on a host and service /** * Bind the internal acceptor using the given host and service. More details - * about what host and service can be are available in the boost asio + * about what host and service can be are available in the Asio * documentation for ip::basic_resolver_query::basic_resolver_query's * constructors. * @@ -609,7 +613,7 @@ class endpoint : public config::socket_type { * @since 0.3.0 */ void start_perpetual() { - m_work = lib::make_shared( + m_work = lib::make_shared( lib::ref(*m_io_service) ); } @@ -639,9 +643,9 @@ class endpoint : public config::socket_type { * needed. */ timer_ptr set_timer(long duration, timer_handler callback) { - timer_ptr new_timer = lib::make_shared( + timer_ptr new_timer = lib::make_shared( *m_io_service, - boost::posix_time::milliseconds(duration) + lib::asio::milliseconds(duration) ); new_timer->async_wait( @@ -667,10 +671,10 @@ class endpoint : public config::socket_type { * @param ec A status code indicating an error, if any. */ void handle_timer(timer_ptr, timer_handler callback, - boost::system::error_code const & ec) + lib::asio::error_code const & ec) { if (ec) { - if (ec == boost::asio::error::operation_aborted) { + if (ec == lib::asio::error::operation_aborted) { callback(make_error_code(transport::error::operation_aborted)); } else { m_elog->write(log::elevel::info, @@ -749,18 +753,18 @@ class endpoint : public config::socket_type { m_elog = e; } - void handle_accept(accept_handler callback, boost::system::error_code const - & boost_ec) + void handle_accept(accept_handler callback, lib::asio::error_code const & + asio_ec) { lib::error_code ret_ec; m_alog->write(log::alevel::devel, "asio::handle_accept"); - if (boost_ec) { - if (boost_ec == boost::system::errc::operation_canceled) { + if (asio_ec) { + if (asio_ec == lib::asio::errc::operation_canceled) { ret_ec = make_error_code(websocketpp::error::operation_canceled); } else { - log_err(log::elevel::info,"asio handle_accept",boost_ec); + log_err(log::elevel::info,"asio handle_accept",asio_ec); ret_ec = make_error_code(error::pass_through); } } @@ -771,14 +775,16 @@ class endpoint : public config::socket_type { /// Initiate a new connection // TODO: there have to be some more failure conditions here void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) { - using namespace boost::asio::ip; + using namespace lib::asio::ip; // Create a resolver if (!m_resolver) { - m_resolver = lib::make_shared( + m_resolver = lib::make_shared( lib::ref(*m_io_service)); } + tcon->set_uri(u); + std::string proxy = tcon->get_proxy(); std::string host; std::string port; @@ -888,11 +894,11 @@ class endpoint : public config::socket_type { } void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer, - connect_handler callback, boost::system::error_code const & ec, - boost::asio::ip::tcp::resolver::iterator iterator) + connect_handler callback, lib::asio::error_code const & ec, + lib::asio::ip::tcp::resolver::iterator iterator) { - if (ec == boost::asio::error::operation_aborted || - dns_timer->expires_from_now().is_negative()) + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(dns_timer->expires_from_now())) { m_alog->write(log::alevel::devel,"async_resolve cancelled"); return; @@ -910,7 +916,7 @@ class endpoint : public config::socket_type { std::stringstream s; s << "Async DNS resolve successful. Results: "; - boost::asio::ip::tcp::resolver::iterator it, end; + lib::asio::ip::tcp::resolver::iterator it, end; for (it = iterator; it != end; ++it) { s << (*it).endpoint() << " "; } @@ -935,7 +941,7 @@ class endpoint : public config::socket_type { ); if (config::enable_multithreading) { - boost::asio::async_connect( + lib::asio::async_connect( tcon->get_raw_socket(), iterator, tcon->get_strand()->wrap(lib::bind( @@ -948,7 +954,7 @@ class endpoint : public config::socket_type { )) ); } else { - boost::asio::async_connect( + lib::asio::async_connect( tcon->get_raw_socket(), iterator, lib::bind( @@ -997,10 +1003,10 @@ class endpoint : public config::socket_type { } void handle_connect(transport_con_ptr tcon, timer_ptr con_timer, - connect_handler callback, boost::system::error_code const & ec) + connect_handler callback, lib::asio::error_code const & ec) { - if (ec == boost::asio::error::operation_aborted || - con_timer->expires_from_now().is_negative()) + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(con_timer->expires_from_now())) { m_alog->write(log::alevel::devel,"async_connect cancelled"); return; diff --git a/websocketpp/transport/asio/security/base.hpp b/websocketpp/transport/asio/security/base.hpp index 543a3533c..dff14508b 100644 --- a/websocketpp/transport/asio/security/base.hpp +++ b/websocketpp/transport/asio/security/base.hpp @@ -34,8 +34,6 @@ #include #include -#include - #include // Interface that sockets/security policies must implement @@ -56,6 +54,7 @@ // Connection // TODO +// set_hostname(std::string hostname) // pre_init(init_handler); // post_init(init_handler); @@ -97,7 +96,10 @@ namespace error { missing_tls_init_handler, /// TLS Handshake Failed - tls_handshake_failed + tls_handshake_failed, + + /// Failed to set TLS SNI hostname + tls_failed_sni_hostname }; } // namespace error @@ -126,6 +128,8 @@ class socket_category : public lib::error_category { return "Required tls_init handler not present."; case error::tls_handshake_failed: return "TLS handshake failed"; + case error::tls_failed_sni_hostname: + return "Failed to set TLS SNI hostname"; default: return "Unknown"; } diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp index 14b6f8b5f..138ea403a 100644 --- a/websocketpp/transport/asio/security/none.hpp +++ b/websocketpp/transport/asio/security/none.hpp @@ -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: @@ -28,12 +28,13 @@ #ifndef WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP #define WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP +#include + #include +#include #include -#include - #include #include @@ -45,13 +46,13 @@ namespace asio { namespace basic_socket { /// The signature of the socket init handler for this socket policy -typedef lib::function +typedef lib::function socket_init_handler; -/// Basic Boost ASIO connection socket component +/// Basic Asio connection socket component /** * transport::asio::basic_socket::connection implements a connection socket - * component using Boost ASIO ip::tcp::socket. + * component using Asio ip::tcp::socket. */ class connection : public lib::enable_shared_from_this { public: @@ -60,12 +61,12 @@ class connection : public lib::enable_shared_from_this { /// Type of a shared pointer to this connection socket component typedef lib::shared_ptr ptr; - /// 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 io_service strand being used - typedef lib::shared_ptr strand_ptr; + /// Type of a pointer to the Asio io_service being used + typedef lib::asio::io_service* io_service_ptr; + /// Type of a pointer to the Asio io_service strand being used + typedef lib::shared_ptr strand_ptr; /// Type of the ASIO socket being used - typedef boost::asio::ip::tcp::socket socket_type; + typedef lib::asio::ip::tcp::socket socket_type; /// Type of a shared pointer to the socket being used. typedef lib::shared_ptr socket_ptr; @@ -91,7 +92,7 @@ class connection : public lib::enable_shared_from_this { /** * The socket initialization handler is called after the socket object is * created but before it is used. This gives the application a chance to - * set any ASIO socket options it needs. + * set any Asio socket options it needs. * * @param h The new socket_init_handler */ @@ -103,7 +104,7 @@ class connection : public lib::enable_shared_from_this { /** * This is used internally. It can also be used to set socket options, etc */ - boost::asio::ip::tcp::socket& get_socket() { + lib::asio::ip::tcp::socket & get_socket() { return *m_socket; } @@ -111,7 +112,7 @@ class connection : public lib::enable_shared_from_this { /** * This is used internally. */ - boost::asio::ip::tcp::socket& get_next_layer() { + lib::asio::ip::tcp::socket & get_next_layer() { return *m_socket; } @@ -119,7 +120,7 @@ class connection : public lib::enable_shared_from_this { /** * This is used internally. It can also be used to set socket options, etc */ - boost::asio::ip::tcp::socket& get_raw_socket() { + lib::asio::ip::tcp::socket & get_raw_socket() { return *m_socket; } @@ -133,16 +134,16 @@ class connection : public lib::enable_shared_from_this { * * @return A string identifying the address of the remote endpoint */ - std::string get_remote_endpoint(lib::error_code &ec) const { + std::string get_remote_endpoint(lib::error_code & ec) const { std::stringstream s; - boost::system::error_code bec; - boost::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(bec); + lib::asio::error_code aec; + lib::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(aec); - if (bec) { + if (aec) { ec = error::make_error_code(error::pass_through); - s << "Error getting remote endpoint: " << bec - << " (" << bec.message() << ")"; + s << "Error getting remote endpoint: " << aec + << " (" << aec.message() << ")"; return s.str(); } else { ec = lib::error_code(); @@ -154,7 +155,7 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * boost::asio components to the io_service + * Asio components to the io_service * * @param service A pointer to the endpoint's io_service * @param strand A shared pointer to the connection's asio strand @@ -166,7 +167,7 @@ class connection : public lib::enable_shared_from_this { return socket::make_error_code(socket::error::invalid_state); } - m_socket = lib::make_shared( + m_socket = lib::make_shared( lib::ref(*service)); m_state = READY; @@ -174,6 +175,19 @@ class connection : public lib::enable_shared_from_this { return lib::error_code(); } + /// Set uri hook + /** + * Called by the transport as a connection is being established to provide + * the uri being connected to to the security/socket layer. + * + * This socket policy doesn't use the uri so it is ignored. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + /// Pre-initialize security policy /** * Called by the transport after a new connection is created to initialize @@ -227,8 +241,8 @@ class connection : public lib::enable_shared_from_this { } void async_shutdown(socket_shutdown_handler h) { - boost::system::error_code ec; - m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both,ec); + lib::asio::error_code ec; + m_socket->shutdown(lib::asio::ip::tcp::socket::shutdown_both, ec); h(ec); } @@ -248,7 +262,7 @@ class connection : public lib::enable_shared_from_this { * @param ec The error code to translate_ec * @return The translated error code */ - lib::error_code translate_ec(boost::system::error_code) { + lib::error_code translate_ec(lib::asio::error_code) { // We don't know any more information about this error so pass through return make_error_code(transport::error::pass_through); } diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index 8434a5c3d..350a49fab 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -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: @@ -30,14 +30,14 @@ #include +#include + +#include +#include #include #include #include -#include -#include -#include - #include #include @@ -49,16 +49,16 @@ namespace asio { namespace tls_socket { /// The signature of the socket_init_handler for this socket policy -typedef lib::function&)> socket_init_handler; +typedef lib::function&)> socket_init_handler; /// The signature of the tls_init_handler for this socket policy -typedef lib::function(connection_hdl)> +typedef lib::function(connection_hdl)> tls_init_handler; /// TLS enabled Boost ASIO connection socket component /** * transport::asio::tls_socket::connection implements a secure connection socket - * component that uses Boost ASIO's ssl::stream to wrap an ip::tcp::socket. + * component that uses Asio's ssl::stream to wrap an ip::tcp::socket. */ class connection : public lib::enable_shared_from_this { public: @@ -68,17 +68,15 @@ class connection : public lib::enable_shared_from_this { typedef lib::shared_ptr ptr; /// Type of the ASIO socket being used - typedef boost::asio::ssl::stream socket_type; + typedef lib::asio::ssl::stream socket_type; /// Type of a shared pointer to the ASIO socket being used typedef lib::shared_ptr socket_ptr; /// Type of a pointer to the ASIO io_service being used - typedef boost::asio::io_service* io_service_ptr; + typedef lib::asio::io_service * io_service_ptr; /// Type of a pointer to the ASIO io_service strand being used - typedef lib::shared_ptr strand_ptr; + typedef lib::shared_ptr strand_ptr; /// Type of a shared pointer to the ASIO TLS context being used - typedef lib::shared_ptr context_ptr; - - typedef boost::system::error_code boost_error; + typedef lib::shared_ptr context_ptr; explicit connection() { //std::cout << "transport::asio::tls_socket::connection constructor" @@ -102,7 +100,7 @@ class connection : public lib::enable_shared_from_this { /** * This is used internally. It can also be used to set socket options, etc */ - socket_type::lowest_layer_type& get_raw_socket() { + socket_type::lowest_layer_type & get_raw_socket() { return m_socket->lowest_layer(); } @@ -110,7 +108,7 @@ class connection : public lib::enable_shared_from_this { /** * This is used internally. */ - socket_type::next_layer_type& get_next_layer() { + socket_type::next_layer_type & get_next_layer() { return m_socket->next_layer(); } @@ -118,7 +116,7 @@ class connection : public lib::enable_shared_from_this { /** * This is used internally. */ - socket_type& get_socket() { + socket_type & get_socket() { return *m_socket; } @@ -157,16 +155,16 @@ class connection : public lib::enable_shared_from_this { * * @return A string identifying the address of the remote endpoint */ - std::string get_remote_endpoint(lib::error_code &ec) const { + std::string get_remote_endpoint(lib::error_code & ec) const { std::stringstream s; - boost::system::error_code bec; - boost::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(bec); + lib::asio::error_code aec; + lib::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(aec); - if (bec) { + if (aec) { ec = error::make_error_code(error::pass_through); - s << "Error getting remote endpoint: " << bec - << " (" << bec.message() << ")"; + s << "Error getting remote endpoint: " << aec + << " (" << aec.message() << ")"; return s.str(); } else { ec = lib::error_code(); @@ -178,7 +176,7 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * boost::asio components to the io_service + * Asio components to the io_service * * @param service A pointer to the endpoint's io_service * @param strand A pointer to the connection's strand @@ -205,6 +203,22 @@ class connection : public lib::enable_shared_from_this { return lib::error_code(); } + /// Set hostname hook + /** + * Called by the transport as a connection is being established to provide + * the hostname being connected to to the security/socket layer. + * + * This socket policy uses the hostname to set the appropriate TLS SNI + * header. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr u) { + m_uri = u; + } + /// Pre-initialize security policy /** * Called by the transport after a new connection is created to initialize @@ -215,6 +229,22 @@ class connection : public lib::enable_shared_from_this { * @param callback Handler to call back with completion information */ void pre_init(init_handler callback) { + // TODO: is this the best way to check whether this function is + // available in the version of OpenSSL being used? + // TODO: consider case where host is an IP address +#if OPENSSL_VERSION_NUMBER >= 0x90812f + if (!m_is_server) { + // For clients on systems with a suitable OpenSSL version, set the + // TLS SNI hostname header so connecting to TLS servers using SNI + // will work. + long res = SSL_set_tlsext_host_name( + get_socket().native_handle(), m_uri->get_host().c_str()); + if (!(1 == res)) { + callback(socket::make_error_code(socket::error::tls_failed_sni_hostname)); + } + } +#endif + if (m_socket_init_handler) { m_socket_init_handler(m_hdl,get_socket()); } @@ -266,8 +296,7 @@ class connection : public lib::enable_shared_from_this { m_hdl = hdl; } - void handle_init(init_handler callback,boost::system::error_code const & ec) - { + void handle_init(init_handler callback,lib::asio::error_code const & ec) { if (ec) { m_ec = socket::make_error_code(socket::error::tls_handshake_failed); } else { @@ -306,7 +335,7 @@ class connection : public lib::enable_shared_from_this { * @return The translated error code */ lib::error_code translate_ec(boost::system::error_code ec) { - if (ec.category() == boost::asio::error::get_ssl_category()) { + if (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); } else { @@ -323,9 +352,9 @@ class connection : public lib::enable_shared_from_this { private: socket_type::handshake_type get_handshake_type() { if (m_is_server) { - return boost::asio::ssl::stream_base::server; + return lib::asio::ssl::stream_base::server; } else { - return boost::asio::ssl::stream_base::client; + return lib::asio::ssl::stream_base::client; } } @@ -333,6 +362,7 @@ class connection : public lib::enable_shared_from_this { strand_ptr m_strand; context_ptr m_context; socket_ptr m_socket; + uri_ptr m_uri; bool m_is_server; lib::error_code m_ec; @@ -342,10 +372,10 @@ class connection : public lib::enable_shared_from_this { tls_init_handler m_tls_init_handler; }; -/// TLS enabled Boost ASIO endpoint socket component +/// TLS enabled Asio endpoint socket component /** * transport::asio::tls_socket::endpoint implements a secure endpoint socket - * component that uses Boost ASIO's ssl::stream to wrap an ip::tcp::socket. + * component that uses Asio's ssl::stream to wrap an ip::tcp::socket. */ class endpoint { public: diff --git a/websocketpp/transport/debug/connection.hpp b/websocketpp/transport/debug/connection.hpp index 7b8c29cbd..36b282a20 100644 --- a/websocketpp/transport/debug/connection.hpp +++ b/websocketpp/transport/debug/connection.hpp @@ -32,6 +32,7 @@ #include +#include #include #include @@ -103,6 +104,20 @@ class connection : public lib::enable_shared_from_this< connection > { return false; } + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * Implementation is optional and can be ignored if the transport has no + * need for this information. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + /// Set human readable remote endpoint address /** * Sets the remote endpoint address returned by `get_remote_endpoint`. This diff --git a/websocketpp/transport/iostream/base.hpp b/websocketpp/transport/iostream/base.hpp index 0103856a9..f87839878 100644 --- a/websocketpp/transport/iostream/base.hpp +++ b/websocketpp/transport/iostream/base.hpp @@ -33,7 +33,10 @@ #include #include +#include + #include +#include namespace websocketpp { namespace transport { @@ -41,10 +44,19 @@ namespace transport { namespace iostream { /// The type and signature of the callback used by iostream transport to write -typedef lib::function +typedef lib::function write_handler; -/// The type and signature of the callback used by iostream transport to signal +/// The type and signature of the callback used by iostream transport to perform +/// vectored writes. +/** + * If a vectored write handler is not set the standard write handler will be + * called multiple times. + */ +typedef lib::function const + & bufs)> vector_write_handler; + +/// The type and signature of the callback used by iostream transport to signal /// a transport shutdown. typedef lib::function shutdown_handler; diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp index c0d090118..81c4f4113 100644 --- a/websocketpp/transport/iostream/connection.hpp +++ b/websocketpp/transport/iostream/connection.hpp @@ -32,6 +32,8 @@ #include +#include + #include #include @@ -105,6 +107,19 @@ class connection : public lib::enable_shared_from_this< connection > { m_output_stream = o; } + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * This transport policy doesn't use the uri so it is ignored. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + /// Overloaded stream input operator /** * Attempts to read input from the given stream into the transport. Bytes @@ -153,7 +168,7 @@ class connection : public lib::enable_shared_from_this< connection > { return this->read_some_impl(buf,len); } - + /// Manual input supply (read all) /** * Similar to read_some, but continues to read until all bytes in the @@ -173,7 +188,7 @@ class connection : public lib::enable_shared_from_this< connection > { size_t read_all(char const * buf, size_t len) { // this serializes calls to external read. scoped_lock_type lock(m_read_mutex); - + size_t total_read = 0; size_t temp_read = 0; @@ -312,25 +327,60 @@ class connection : public lib::enable_shared_from_this< connection > { timer_ptr set_timer(long, timer_handler) { return timer_ptr(); } - + /// Sets the write handler /** * The write handler is called when the iostream transport receives data * that needs to be written to the appropriate output location. This handler * can be used in place of registering an ostream for output. * - * The signature of the handler is + * The signature of the handler is * `lib::error_code (connection_hdl, char const *, size_t)` The * code returned will be reported and logged by the core library. * + * See also, set_vector_write_handler, for an optional write handler that + * allows more efficient handling of multiple writes at once. + * + * @see set_vector_write_handler + * * @since 0.5.0 * - * @param h The handler to call on connection shutdown. + * @param h The handler to call when data is to be written. */ void set_write_handler(write_handler h) { m_write_handler = h; } - + + /// Sets the vectored write handler + /** + * The vectored write handler is called when the iostream transport receives + * multiple chunks of data that need to be written to the appropriate output + * location. This handler can be used in conjunction with the write_handler + * in place of registering an ostream for output. + * + * The sequence of buffers represents bytes that should be written + * consecutively and it is suggested to group the buffers into as few next + * layer packets as possible. Vector write is used to allow implementations + * that support it to coalesce writes into a single TCP packet or TLS + * segment for improved efficiency. + * + * This is an optional handler. If it is not defined then multiple calls + * will be made to the standard write handler. + * + * The signature of the handler is + * `lib::error_code (connection_hdl, std::vector + * const & bufs)`. The code returned will be reported and logged by the core + * library. The `websocketpp::transport::buffer` type is a struct with two + * data members. buf (char const *) and len (size_t). + * + * @since 0.6.0 + * + * @param h The handler to call when vectored data is to be written. + */ + void set_vector_write_handler(vector_write_handler h) { + m_vector_write_handler = h; + } + /// Sets the shutdown handler /** * The shutdown handler is called when the iostream transport receives a @@ -434,7 +484,7 @@ class connection : public lib::enable_shared_from_this< connection > { * @param len number of bytes to write * @param handler Callback to invoke with operation status. */ - void async_write(char const * buf, size_t len, transport::write_handler + void async_write(char const * buf, size_t len, transport::write_handler handler) { m_alog.write(log::alevel::devel,"iostream_con async_write"); @@ -444,7 +494,7 @@ class connection : public lib::enable_shared_from_this< connection > { if (m_output_stream) { m_output_stream->write(buf,len); - + if (m_output_stream->bad()) { ec = make_error_code(error::bad_stream); } @@ -492,17 +542,19 @@ class connection : public lib::enable_shared_from_this< connection > { break; } } + } else if (m_vector_write_handler) { + ec = m_vector_write_handler(m_connection_hdl, bufs); } else if (m_write_handler) { std::vector::const_iterator it; for (it = bufs.begin(); it != bufs.end(); it++) { ec = m_write_handler(m_connection_hdl, (*it).buf, (*it).len); if (ec) {break;} } - + } else { ec = make_error_code(error::output_stream_required); } - + handler(ec); } @@ -540,11 +592,11 @@ class connection : public lib::enable_shared_from_this< connection > { */ void async_shutdown(transport::shutdown_handler handler) { lib::error_code ec; - + if (m_shutdown_handler) { ec = m_shutdown_handler(m_connection_hdl); } - + handler(ec); } private: @@ -636,6 +688,7 @@ class connection : public lib::enable_shared_from_this< connection > { std::ostream * m_output_stream; connection_hdl m_connection_hdl; write_handler m_write_handler; + vector_write_handler m_vector_write_handler; shutdown_handler m_shutdown_handler; bool m_reading; diff --git a/websocketpp/transport/stub/connection.hpp b/websocketpp/transport/stub/connection.hpp index 3e501a963..59bd4a0a4 100644 --- a/websocketpp/transport/stub/connection.hpp +++ b/websocketpp/transport/stub/connection.hpp @@ -103,6 +103,20 @@ class connection : public lib::enable_shared_from_this< connection > { return false; } + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * Implementation is optional and can be ignored if the transport has no + * need for this information. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + /// Set human readable remote endpoint address /** * Sets the remote endpoint address returned by `get_remote_endpoint`. This diff --git a/websocketpp/version.hpp b/websocketpp/version.hpp index 6f9397bad..56a0a76d6 100644 --- a/websocketpp/version.hpp +++ b/websocketpp/version.hpp @@ -42,9 +42,9 @@ namespace websocketpp { /// Library major version number static int const major_version = 0; /// Library minor version number -static int const minor_version = 5; +static int const minor_version = 6; /// Library patch version number -static int const patch_version = 1; +static int const patch_version = 0; /// Library pre-release flag /** * This is a textual flag indicating the type and number for pre-release @@ -53,7 +53,7 @@ static int const patch_version = 1; static char const prerelease_flag[] = "permessagedeflate"; /// Default user agent string -static char const user_agent[] = "WebSocketpp/0.5.1-permessagedeflate"; +static char const user_agent[] = "WebSocketpp/0.6.0-permessagedeflate"; } // namespace websocketpp