diff --git a/CMakeLists.txt b/CMakeLists.txt index 9043db327..5fefcbf01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,15 +2,57 @@ ############ Setup project and cmake # Minimum cmake requirement. We should require a quite recent # cmake for the dependency find macros etc. to be up to date. -cmake_minimum_required (VERSION 2.8.8) - +cmake_minimum_required (VERSION 3.5) +project(websocketpp VERSION 0.9.0) ############ Paths set (WEBSOCKETPP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) set (WEBSOCKETPP_INCLUDE ${WEBSOCKETPP_ROOT}/websocketpp) set (WEBSOCKETPP_BUILD_ROOT ${CMAKE_CURRENT_BINARY_DIR}) -set (WEBSOCKETPP_BIN ${WEBSOCKETPP_BUILD_ROOT}/bin) -set (WEBSOCKETPP_LIB ${WEBSOCKETPP_BUILD_ROOT}/lib) +set (WEBSOCKETPP_ENCODING_LIBS "") +set (WEBSOCKETPP_ENCODING_DEFS "") + +# Include our cmake macros +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +include (CMakeHelpers) + +############ Encoding/decoding support + +set(WEBSOCKETPP_WITH_GZIP FALSE CACHE BOOL "If true, build support for LZ77 compression. No requirements - added to code via https://github.com/ivan-tkatchev/yalz77.") +set(WEBSOCKETPP_WITH_DEFLATE FALSE CACHE BOOL "If true, build support for deflate compression. Requires zlib.") +if (WEBSOCKETPP_WITH_GZIP OR WEBSOCKETPP_WITH_DEFLATE) + find_package(ZLIB REQUIRED) + if (ZLIB_FOUND) + list(APPEND WEBSOCKETPP_ENCODING_LIBS ${ZLIB_LIBRARIES}) + list(APPEND WEBSOCKETPP_INCLUDE ${ZLIB_INCLUDE_DIRS}) + if (WEBSOCKETPP_WITH_DEFLATE) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_DEFLATE=1) + endif () + if (WEBSOCKETPP_WITH_GZIP) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_GZIP=1) + endif () + endif () +endif () + +set(WEBSOCKETPP_WITH_BROTLI FALSE CACHE BOOL "If true, build support for brotli compression. Requires brotli.") +if (WEBSOCKETPP_WITH_BROTLI) + find_package(Brotli REQUIRED) + if (BROTLI_FOUND) + list(APPEND WEBSOCKETPP_ENCODING_LIBS ${BROTLI_LIBRARIES}) + list(APPEND WEBSOCKETPP_INCLUDE ${BROTLI_INCLUDE_DIRS}) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_BROTLI=1) + endif () +endif () + +set(WEBSOCKETPP_WITH_ZSTD FALSE CACHE BOOL "If true, build support for zstd compression. Requires zstd.") +if (WEBSOCKETPP_WITH_ZSTD) + find_package(zstd REQUIRED) + if (ZSTD_FOUND) + list(APPEND WEBSOCKETPP_ENCODING_LIBS ${ZSTD_LIBRARIES}) + list(APPEND WEBSOCKETPP_INCLUDE ${ZSTD_INCLUDE_DIRS}) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_ZSTD=1) + endif () +endif () # CMake install step prefix. I assume linux users want the prefix to # be the default /usr or /usr/local so this is only adjusted on Windows. @@ -21,22 +63,6 @@ if (WIN32) set (CMAKE_INSTALL_PREFIX "${WEBSOCKETPP_ROOT}/install" CACHE PATH "") endif () -############ Project name and version -set (WEBSOCKETPP_MAJOR_VERSION 0) -set (WEBSOCKETPP_MINOR_VERSION 8) -set (WEBSOCKETPP_PATCH_VERSION 3) -set (WEBSOCKETPP_VERSION ${WEBSOCKETPP_MAJOR_VERSION}.${WEBSOCKETPP_MINOR_VERSION}.${WEBSOCKETPP_PATCH_VERSION}) - -if(POLICY CMP0048) - cmake_policy(GET CMP0048 _version_policy) -endif() - -if(_version_allowed STREQUAL NEW) - project (websocketpp VERSION ${WEBSOCKETPP_VERSION}) -else() - project (websocketpp) -endif() - set_property(GLOBAL PROPERTY USE_FOLDERS ON) include(GNUInstallDirs) @@ -66,10 +92,6 @@ endif () # Disable unnecessary build types set (CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo;Debug" CACHE STRING "Configurations" FORCE) -# Include our cmake macros -set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -include (CMakeHelpers) - ############ Build customization @@ -254,9 +276,6 @@ endif() ############ Add projects -# Add main library -add_subdirectory (websocketpp) - # Add examples if (BUILD_EXAMPLES) include_subdirs ("examples") @@ -269,8 +288,6 @@ endif () print_used_build_config() -export (PACKAGE websocketpp) - include(CMakePackageConfigHelpers) configure_package_config_file(websocketpp-config.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-config.cmake" @@ -288,3 +305,8 @@ install (FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-configVersion.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) +add_library(websocketpp INTERFACE) +target_link_libraries(websocketpp INTERFACE ${WEBSOCKETPP_ENCODING_LIBS}) +target_compile_definitions(websocketpp INTERFACE ${WEBSOCKETPP_ENCODING_DEFS}) +target_include_directories(websocketpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + diff --git a/SConstruct b/SConstruct index c5ae19369..f87e4e8b9 100644 --- a/SConstruct +++ b/SConstruct @@ -273,8 +273,8 @@ subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',varia # telemetry_server telemetry_server = SConscript('#/examples/telemetry_server/SConscript',variant_dir = builddir + 'telemetry_server',duplicate = 0) -# external_io_service -external_io_service = SConscript('#/examples/external_io_service/SConscript',variant_dir = builddir + 'external_io_service',duplicate = 0) +# external_io_context +external_io_context = SConscript('#/examples/external_io_context/SConscript',variant_dir = builddir + 'external_io_context',duplicate = 0) if not env['PLATFORM'].startswith('win'): # iostream_server diff --git a/changelog.md b/changelog.md index 9e0b0a278..d36d3ab2b 100644 --- a/changelog.md +++ b/changelog.md @@ -211,7 +211,7 @@ in the installer and test system. - Improvement: Removes use of empty strings ("") in favor of `string::clear()` and `string::empty()`. This avoids generating unnecessary temporary objects. #468 Thank you Vladislav Yaroslavlev for reporting and a patch. -- Documentation: Adds an example demonstrating the use of external `io_service` +- Documentation: Adds an example demonstrating the use of external `io_context` - Documentation: Adds a simple `echo_client` example. - Documentation: Begins migration of the web based user manual into Doxygen. - Bug: Fix memory leak when `init_asio` produces an error. #454 Thank you Mark @@ -232,7 +232,7 @@ in the installer and test system. - Bug: Fix an issue where TLS includes were broken for Asio Standalone builds. Thank you giachi and Bastien Brunnenstein for reporting. #491 - Bug: Remove the use of cached read and write handlers in the Asio transport. - This feature caused memory leaks when the `io_service` the connection was + This feature caused memory leaks when the `io_context` the connection was running on was abruptly stopped. There isn't a clean and safe way of using this optimization without global state and the associated locks. The locks perform worse. Thank you Xavier Gibert for reporting, test cases, and code. @@ -315,7 +315,7 @@ in the installer and test system. - Improvement: Message payload logging now prints text for text messages rather than binary. - Improvement: Overhaul of handshake state machine. Should make it impossible - for exceptions to bubble out of transport methods like `io_service::run`. + for exceptions to bubble out of transport methods like `io_context::run`. - Improvement: Overhaul of handshake error reporting. Fail handler error codes will be more detailed and precise. Adds new [fail] and [http] logging channels that log failed websocket connections and successful HTTP connections @@ -385,7 +385,7 @@ in the installer and test system. 0.3.0 - 2014-08-10 - Feature: Adds `start_perpetual` and `stop_perpetual` methods to asio transport - These may be used to replace manually managed `asio::io_service::work` objects + These may be used to replace manually managed `asio::io_context::work` objects - Feature: Allow setting pong and handshake timeouts at runtime. - Feature: Allows changing the listen backlog queue length. - Feature: Split tcp init into pre and post init. @@ -427,7 +427,7 @@ in the installer and test system. reference counted pointers. #310 Thank you otaras for reporting. - Bug: Fix issue with const endpoint accessors (such as `get_user_agent`) not compiling due to non-const mutex use. #292 Thank you logofive for reporting. -- Bug: Fix handler allocation crash with multithreaded `io_service`. +- Bug: Fix handler allocation crash with multithreaded `io_context`. - Bug: Fixes incorrect whitespace handling in header parsing. #301 Thank you Wolfram Schroers for reporting - Bug: Fix a crash when parsing empty HTTP headers. Thank you Thingol for @@ -457,7 +457,7 @@ in the installer and test system. - Updates bundled sha1 library to one with a cleaner interface and more straight-forward license. Thank you lotodore for reporting and Evgeni Golov for reviewing. #294 -- Re-introduces strands to asio transport, allowing `io_service` thread pools to +- Re-introduces strands to asio transport, allowing `io_context` thread pools to be used (with some limitations). - Removes endpoint code that kept track of a connection list that was never used anywhere. Removes a lock and reduces connection creation/deletion complexity @@ -482,7 +482,7 @@ in the installer and test system. - Refactors `asio_transport` endpoint and adds full documentation and exception free varients of all methods. - Removes `asio_transport` endpoint method cancel(). Use `stop_listen()` instead -- Wrap internal `io_service` `run_one()` method +- Wrap internal `io_context` `run_one()` method - Suppress error when trying to shut down a connection that was already closed 0.3.0-alpha3 - 2013-07-16 diff --git a/cmake/CMakeHelpers.cmake b/cmake/CMakeHelpers.cmake index f6036325b..c75f86346 100644 --- a/cmake/CMakeHelpers.cmake +++ b/cmake/CMakeHelpers.cmake @@ -1,25 +1,25 @@ # Print build configuration macro (print_used_build_config) - message ("\n=========== Used Build Configuration =============\n") + message (STATUS "=========== Websocketpp Build Configuration =============") message (STATUS "ENABLE_CPP11 = " ${ENABLE_CPP11}) message (STATUS "BUILD_EXAMPLES = " ${BUILD_EXAMPLES}) message (STATUS "BUILD_TESTS = " ${BUILD_TESTS}) - message ("") + message (STATUS "") message (STATUS "WEBSOCKETPP_ROOT = " ${WEBSOCKETPP_ROOT}) message (STATUS "WEBSOCKETPP_BIN = " ${WEBSOCKETPP_BIN}) message (STATUS "WEBSOCKETPP_LIB = " ${WEBSOCKETPP_LIB}) message (STATUS "Install prefix = " ${CMAKE_INSTALL_PREFIX}) - message ("") + message (STATUS "") message (STATUS "WEBSOCKETPP_BOOST_LIBS = ${WEBSOCKETPP_BOOST_LIBS}") message (STATUS "WEBSOCKETPP_PLATFORM_LIBS = ${WEBSOCKETPP_PLATFORM_LIBS}") message (STATUS "WEBSOCKETPP_PLATFORM_TLS_LIBS = ${WEBSOCKETPP_PLATFORM_TLS_LIBS}") - message ("") + message (STATUS "") message (STATUS "OPENSSL_FOUND = ${OPENSSL_FOUND}") message (STATUS "OPENSSL_INCLUDE_DIR = ${OPENSSL_INCLUDE_DIR}") message (STATUS "OPENSSL_LIBRARIES = ${OPENSSL_LIBRARIES}") message (STATUS "OPENSSL_VERSION = ${OPENSSL_VERSION}") - message ("") + message (STATUS "===========================================================") endmacro () # Adds the given folder_name into the source files of the current project. diff --git a/cmake/FindBrotli.cmake b/cmake/FindBrotli.cmake new file mode 100644 index 000000000..1617e1afa --- /dev/null +++ b/cmake/FindBrotli.cmake @@ -0,0 +1,45 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +include(FindPackageHandleStandardArgs) + +find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") + +find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon) +find_library(BROTLIDEC_LIBRARY NAMES brotlidec) +find_library(BROTLIENC_LIBRARY NAMES brotlienc) + +find_package_handle_standard_args(Brotli + FOUND_VAR + BROTLI_FOUND + REQUIRED_VARS + BROTLIDEC_LIBRARY + BROTLIENC_LIBRARY + BROTLICOMMON_LIBRARY + BROTLI_INCLUDE_DIR + FAIL_MESSAGE + "Could NOT find Brotli" +) + +set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) +set(BROTLI_LIBRARIES ${BROTLICOMMON_LIBRARY} ${BROTLIDEC_LIBRARY} ${BROTLIENC_LIBRARY}) \ No newline at end of file diff --git a/cmake/Findzstd.cmake b/cmake/Findzstd.cmake new file mode 100644 index 000000000..7ef48ef1c --- /dev/null +++ b/cmake/Findzstd.cmake @@ -0,0 +1,29 @@ +# - Find zstd +# Find the zstd compression library and includes +# +# ZSTD_INCLUDE_DIRS - where to find zstd.h, etc. +# ZSTD_LIBRARIES - List of libraries when using zstd. +# ZSTD_FOUND - True if zstd found. + +find_path(ZSTD_INCLUDE_DIRS + NAMES zstd.h + HINTS ${zstd_ROOT_DIR}/include) + +find_library(ZSTD_LIBRARIES + NAMES zstd + HINTS ${zstd_ROOT_DIR}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(zstd DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS) + +mark_as_advanced( + ZSTD_LIBRARIES + ZSTD_INCLUDE_DIRS) + +if(ZSTD_FOUND AND NOT (TARGET zstd::zstd)) + add_library (zstd::zstd UNKNOWN IMPORTED) + set_target_properties(zstd::zstd + PROPERTIES + IMPORTED_LOCATION ${ZSTD_LIBRARIES} + INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIRS}) +endif() \ No newline at end of file diff --git a/docs/faq.dox b/docs/faq.dox index 9f417ec41..c89e85b61 100644 --- a/docs/faq.dox +++ b/docs/faq.dox @@ -29,19 +29,19 @@ Note: some browsers will allow the connection to continue if they requested a su ### How do I cleanly exit an Asio transport based program -The Asio transport based clients and servers use the Asio library's underlying `io_service` to handle asyncronous networking operations. The standard behavior of the io_service is to run until there are no async operations left and then return. WebSocket++, when using the Asio transport, behaves like a standard Asio application. If you want your WebSocket++/Asio based program to stop network operations and cleanly close all sockets you will want to do the following: +The Asio transport based clients and servers use the Asio library's underlying `io_context` to handle asyncronous networking operations. The standard behavior of the io_context is to run until there are no async operations left and then return. WebSocket++, when using the Asio transport, behaves like a standard Asio application. If you want your WebSocket++/Asio based program to stop network operations and cleanly close all sockets you will want to do the following: - For servers, call `websocketpp::transport::asio::endpoint::stop_listening` to initiate the closing of the server listening socket. - For clients, if you have engaged perpetual mode with `websocketpp::transport::asio::endpoint::start_perpetual`, disable it with `websocketpp::transport::asio::endpoint::stop_perpetual`. - For both, run `websocketpp::endpoint::close` or `websocketpp::connection::close` on all currently outstanding connections. This will initiate the WebSocket closing handshake for these connections -- Wait. Asio is asyncronous. When the calls to the above methods (stop_listening, close, etc) complete the server *will still be listening*, the connections *will still be active* until the io_service gets around to asyncronously processing the socket and WebSocket protocol closing handshakes. The `io_service::run` method will exit cleanly and automatically when all operations are complete. +- Wait. Asio is asyncronous. When the calls to the above methods (stop_listening, close, etc) complete the server *will still be listening*, the connections *will still be active* until the io_context gets around to asyncronously processing the socket and WebSocket protocol closing handshakes. The `io_context::run` method will exit cleanly and automatically when all operations are complete. -__WARNING__: Asio's `io_service` has a method called `stop`. WebSocket++ wraps this method as `websocketpp::transport::asio::endpoint::stop`. While this operation has a benign sounding name, it is a powerful and destructive operation that should only be used in special cases. If you are using `io_service::stop` or `endpoint::stop` without a very good reason your program is likely broken and may exhibit erratic behavior. Specifically, `io_service::stop` stops the processing of events entirely. This does not give current operations (such as socket closing handshakes) the opportunity to finish. It will leave your sockets in a dangling state that may invoke operating system level timeouts or other errors. +__WARNING__: Asio's `io_context` has a method called `stop`. WebSocket++ wraps this method as `websocketpp::transport::asio::endpoint::stop`. While this operation has a benign sounding name, it is a powerful and destructive operation that should only be used in special cases. If you are using `io_context::stop` or `endpoint::stop` without a very good reason your program is likely broken and may exhibit erratic behavior. Specifically, `io_context::stop` stops the processing of events entirely. This does not give current operations (such as socket closing handshakes) the opportunity to finish. It will leave your sockets in a dangling state that may invoke operating system level timeouts or other errors. __Special cases__: -- If your client uses the `start_perpetual` method it will prevent the io_service from exiting even if it has nothing to do. This is useful if you want a client endpoint to idle in the background to allow new connections to be formed on demand rather than generating a new endpoint for each. -- If you are using an external io_service and/or are placing non-WebSocket++ operations on the `io_service` those operations may keep the `io_service` open even after all WebSocket++ operations have completed. -- If you are using `poll`/`poll_one`/`run_one` or otherwise manually driving the `io_service` event loop you may need to adjust usage to make sure you are correctly recognizing the "done with work" and "not done but idling / `io_service::work`" cases. +- If your client uses the `start_perpetual` method it will prevent the io_context from exiting even if it has nothing to do. This is useful if you want a client endpoint to idle in the background to allow new connections to be formed on demand rather than generating a new endpoint for each. +- If you are using an external io_context and/or are placing non-WebSocket++ operations on the `io_context` those operations may keep the `io_context` open even after all WebSocket++ operations have completed. +- If you are using `poll`/`poll_one`/`run_one` or otherwise manually driving the `io_context` event loop you may need to adjust usage to make sure you are correctly recognizing the "done with work" and "not done but idling / `io_context::work`" cases. ### Is there a way to check the validity of a `connection_hdl`? diff --git a/docs/simple_broadcast_server.cpp b/docs/simple_broadcast_server.cpp index 955d71115..64eb1b60f 100644 --- a/docs/simple_broadcast_server.cpp +++ b/docs/simple_broadcast_server.cpp @@ -20,15 +20,15 @@ class broadcast_server { m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { m_connections.erase(hdl); } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { for (auto it : m_connections) { m_server.send(it,msg); } diff --git a/docs/simple_count_server_thread.cpp b/docs/simple_count_server_thread.cpp index a30af9948..cfd2ade17 100644 --- a/docs/simple_count_server_thread.cpp +++ b/docs/simple_count_server_thread.cpp @@ -19,12 +19,12 @@ class count_server { m_server.set_close_handler(bind(&count_server::on_close,this,_1)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { std::lock_guard lock(m_mutex); m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { std::lock_guard lock(m_mutex); m_connections.erase(hdl); } diff --git a/examples/associative_storage/associative_storage.cpp b/examples/associative_storage/associative_storage.cpp index ea5956c6d..9a9e08531 100644 --- a/examples/associative_storage/associative_storage.cpp +++ b/examples/associative_storage/associative_storage.cpp @@ -27,7 +27,7 @@ class print_server { m_server.set_message_handler(bind(&print_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { connection_data data; data.sessionid = m_next_sessionid++; @@ -36,7 +36,7 @@ class print_server { m_connections[hdl] = data; } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { connection_data& data = get_data_from_hdl(hdl); std::cout << "Closing connection " << data.name @@ -50,7 +50,7 @@ class print_server { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { connection_data& data = get_data_from_hdl(hdl); if (data.name.empty()) { @@ -63,7 +63,7 @@ class print_server { } } - connection_data& get_data_from_hdl(connection_hdl hdl) { + connection_data& get_data_from_hdl(connection_hdl_ref hdl) { auto it = m_connections.find(hdl); if (it == m_connections.end()) { diff --git a/examples/broadcast_server/broadcast_server.cpp b/examples/broadcast_server/broadcast_server.cpp index 35976d35a..88b07a628 100644 --- a/examples/broadcast_server/broadcast_server.cpp +++ b/examples/broadcast_server/broadcast_server.cpp @@ -75,7 +75,7 @@ class broadcast_server { return; } - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop //try { m_server.run(); //} catch (const std::exception & e) { @@ -83,7 +83,7 @@ class broadcast_server { //} } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { { lock_guard guard(m_action_lock); //std::cout << "on_open" << std::endl; @@ -92,7 +92,7 @@ class broadcast_server { m_action_cond.notify_one(); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { { lock_guard guard(m_action_lock); //std::cout << "on_close" << std::endl; @@ -101,7 +101,7 @@ class broadcast_server { m_action_cond.notify_one(); } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { // queue message up for sending by processing thread { lock_guard guard(m_action_lock); diff --git a/examples/debug_client/debug_client.cpp b/examples/debug_client/debug_client.cpp index 457c24934..2c7d94b0f 100644 --- a/examples/debug_client/debug_client.cpp +++ b/examples/debug_client/debug_client.cpp @@ -82,16 +82,16 @@ class perftest { m_endpoint.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop m_start = std::chrono::high_resolution_clock::now(); m_endpoint.run(); } - void on_socket_init(websocketpp::connection_hdl) { + void on_socket_init(websocketpp::connection_hdl_ref) { m_socket_init = std::chrono::high_resolution_clock::now(); } - context_ptr on_tls_init(websocketpp::connection_hdl) { + context_ptr on_tls_init(websocketpp::connection_hdl_ref) { m_tls_init = std::chrono::high_resolution_clock::now(); context_ptr ctx = websocketpp::lib::make_shared(boost::asio::ssl::context::tlsv1); @@ -106,7 +106,7 @@ class perftest { return ctx; } - void on_fail(websocketpp::connection_hdl hdl) { + void on_fail(websocketpp::connection_hdl_ref hdl) { client::connection_ptr con = m_endpoint.get_con_from_hdl(hdl); std::cout << "Fail handler" << std::endl; @@ -118,15 +118,15 @@ class perftest { std::cout << con->get_ec() << " - " << con->get_ec().message() << std::endl; } - void on_open(websocketpp::connection_hdl hdl) { + void on_open(websocketpp::connection_hdl_ref hdl) { m_open = std::chrono::high_resolution_clock::now(); m_endpoint.send(hdl, "", websocketpp::frame::opcode::text); } - void on_message(websocketpp::connection_hdl hdl, message_ptr) { + void on_message(websocketpp::connection_hdl_ref hdl, message_ptr) { m_message = std::chrono::high_resolution_clock::now(); m_endpoint.close(hdl,websocketpp::close::status::going_away,""); } - void on_close(websocketpp::connection_hdl) { + void on_close(websocketpp::connection_hdl_ref) { m_close = std::chrono::high_resolution_clock::now(); std::cout << "Socket Init: " << std::chrono::duration_cast(m_socket_init-m_start).count() << std::endl; diff --git a/examples/debug_server/debug_server.cpp b/examples/debug_server/debug_server.cpp index 8d1ff0ee6..b76826fd5 100644 --- a/examples/debug_server/debug_server.cpp +++ b/examples/debug_server/debug_server.cpp @@ -93,12 +93,12 @@ using websocketpp::lib::bind; // pull out the type of messages sent by our config typedef server::message_ptr message_ptr; -bool validate(server *, websocketpp::connection_hdl) { +session::validation::value validate(server *, websocketpp::connection_hdl_ref) { //sleep(6); - return true; + return session::validation::accept; } -void on_http(server* s, websocketpp::connection_hdl hdl) { +void on_http(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); std::string res = con->get_request_body(); @@ -110,18 +110,18 @@ void on_http(server* s, websocketpp::connection_hdl hdl) { con->set_status(websocketpp::http::status_code::ok); } -void on_fail(server* s, websocketpp::connection_hdl hdl) { +void on_fail(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); std::cout << "Fail handler: " << con->get_ec() << " " << con->get_ec().message() << std::endl; } -void on_close(websocketpp::connection_hdl) { +void on_close(websocketpp::connection_hdl_ref) { std::cout << "Close handler" << std::endl; } // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; @@ -162,7 +162,7 @@ int main() { // Start the server accept loop echo_server.start_accept(); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop echo_server.run(); } catch (websocketpp::exception const & e) { std::cout << e.what() << std::endl; diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index 59af6614f..58ae3fc35 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -41,7 +41,7 @@ typedef websocketpp::config::asio_client::message_type::ptr message_ptr; // This message handler will be invoked once for each incoming message. It // prints the message and then sends a copy of the message back to the server. -void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(client* c, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; @@ -87,7 +87,7 @@ int main(int argc, char* argv[]) { // exchanged until the event loop starts running in the next line. c.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop // this will cause a single connection to be made to the server. c.run() // will exit when this connection is closed. c.run(); diff --git a/examples/echo_server/echo_server.cpp b/examples/echo_server/echo_server.cpp index aca3f6a9a..89dd662bd 100644 --- a/examples/echo_server/echo_server.cpp +++ b/examples/echo_server/echo_server.cpp @@ -15,7 +15,7 @@ using websocketpp::lib::error_code; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; @@ -62,7 +62,7 @@ int main() { // Start the server accept loop echo_server.start_accept(&on_end_accept); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop echo_server.run(); } catch (websocketpp::exception const & e) { std::cout << e.what() << std::endl; diff --git a/examples/echo_server_both/echo_server_both.cpp b/examples/echo_server_both/echo_server_both.cpp index 06b50514a..24075729d 100644 --- a/examples/echo_server_both/echo_server_both.cpp +++ b/examples/echo_server_both/echo_server_both.cpp @@ -20,7 +20,7 @@ typedef websocketpp::lib::shared_ptr context_ptr; // The shared on_message handler takes a template parameter so the function can // resolve any endpoint dependent types like message_ptr or connection_ptr template -void on_message(EndpointType* s, websocketpp::connection_hdl hdl, +void on_message(EndpointType* s, websocketpp::connection_hdl_ref hdl, typename EndpointType::message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() @@ -46,7 +46,7 @@ std::string get_password() { return "test"; } -context_ptr on_tls_init(websocketpp::connection_hdl hdl) { +context_ptr on_tls_init(websocketpp::connection_hdl_ref hdl) { std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl; context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1)); @@ -65,13 +65,13 @@ context_ptr on_tls_init(websocketpp::connection_hdl hdl) { } int main() { - // set up an external io_service to run both endpoints on. This is not + // set up an external io_context to run both endpoints on. This is not // strictly necessary, but simplifies thread management a bit. - boost::asio::io_service ios; + boost::asio::io_context ios; // set up plain endpoint server_plain endpoint_plain; - // initialize asio with our external io_service rather than an internal one + // initialize asio with our external io_context rather than an internal one endpoint_plain.init_asio(&ios); endpoint_plain.set_message_handler( bind(&on_message,&endpoint_plain,::_1,::_2)); @@ -89,6 +89,6 @@ int main() { endpoint_tls.listen(443); endpoint_tls.start_accept(&on_end_accept); - // Start the ASIO io_service run loop running both endpoints + // Start the ASIO io_context run loop running both endpoints ios.run(); } diff --git a/examples/echo_server_tls/echo_server_tls.cpp b/examples/echo_server_tls/echo_server_tls.cpp index ef14a4a22..cad719890 100644 --- a/examples/echo_server_tls/echo_server_tls.cpp +++ b/examples/echo_server_tls/echo_server_tls.cpp @@ -51,7 +51,7 @@ using websocketpp::lib::error_code; typedef websocketpp::config::asio::message_type::ptr message_ptr; typedef websocketpp::lib::shared_ptr context_ptr; -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; @@ -70,7 +70,7 @@ void on_end_accept(error_code lib_ec, error_code trans_ec) { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } -void on_http(server* s, websocketpp::connection_hdl hdl) { +void on_http(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); con->set_body("Hello World!"); @@ -88,7 +88,7 @@ enum tls_mode { MOZILLA_MODERN = 2 }; -context_ptr on_tls_init(tls_mode mode, websocketpp::connection_hdl hdl) { +context_ptr on_tls_init(tls_mode mode, websocketpp::connection_hdl_ref hdl) { namespace asio = websocketpp::lib::asio; std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl; @@ -155,7 +155,7 @@ int main() { // Start the server accept loop echo_server.start_accept(&on_end_accept); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop echo_server.run(); } diff --git a/examples/enriched_storage/enriched_storage.cpp b/examples/enriched_storage/enriched_storage.cpp index 63a2e8e90..0d9d9b4e7 100644 --- a/examples/enriched_storage/enriched_storage.cpp +++ b/examples/enriched_storage/enriched_storage.cpp @@ -46,13 +46,13 @@ class print_server { m_server.set_message_handler(bind(&print_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { connection_ptr con = m_server.get_con_from_hdl(hdl); con->sessionid = m_next_sessionid++; } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { connection_ptr con = m_server.get_con_from_hdl(hdl); std::cout << "Closing connection " << con->name @@ -64,7 +64,7 @@ class print_server { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { connection_ptr con = m_server.get_con_from_hdl(hdl); if (con->name.empty()) { diff --git a/examples/external_io_service/CMakeLists.txt b/examples/external_io_context/CMakeLists.txt similarity index 87% rename from examples/external_io_service/CMakeLists.txt rename to examples/external_io_context/CMakeLists.txt index 5223da1be..a0e89ec96 100644 --- a/examples/external_io_service/CMakeLists.txt +++ b/examples/external_io_context/CMakeLists.txt @@ -2,7 +2,7 @@ file (GLOB SOURCE_FILES *.cpp) file (GLOB HEADER_FILES *.hpp) -init_target (external_io_service) +init_target (external_io_context) build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES}) diff --git a/examples/external_io_service/SConscript b/examples/external_io_context/SConscript similarity index 72% rename from examples/external_io_service/SConscript rename to examples/external_io_context/SConscript index 0abf3e175..c6cd9740d 100644 --- a/examples/external_io_service/SConscript +++ b/examples/external_io_context/SConscript @@ -15,9 +15,9 @@ prgs = [] # if a C++11 environment is available build using that, otherwise use boost if 'WSPP_CPP11_ENABLED' in env_cpp11: ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] - prgs += env_cpp11.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS) + prgs += env_cpp11.Program('external_io_context', ["external_io_context.cpp"], LIBS = ALL_LIBS) else: ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] - prgs += env.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS) + prgs += env.Program('external_io_context', ["external_io_context.cpp"], LIBS = ALL_LIBS) Return('prgs') diff --git a/examples/external_io_service/external_io_service.cpp b/examples/external_io_context/external_io_context.cpp similarity index 93% rename from examples/external_io_service/external_io_service.cpp rename to examples/external_io_context/external_io_context.cpp index 6aa23985e..df32c969a 100644 --- a/examples/external_io_service/external_io_service.cpp +++ b/examples/external_io_context/external_io_context.cpp @@ -39,7 +39,7 @@ using websocketpp::lib::error_code; typedef websocketpp::server ws_echo_server; // Define a callback to handle incoming messages -void on_message(ws_echo_server* s, websocketpp::connection_hdl hdl, ws_echo_server::message_ptr msg) { +void on_message(ws_echo_server* s, websocketpp::connection_hdl_ref hdl, ws_echo_server::message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; @@ -66,7 +66,7 @@ void on_end_accept(error_code lib_ec, error_code trans_ec) { } int main() { - asio::io_service service; + asio::io_context service; // Add a TCP echo server on port 9003 tcp_echo_server custom_http_server(service, 9003); @@ -77,7 +77,7 @@ int main() { ws_server.clear_access_channels(websocketpp::log::alevel::frame_payload); // The only difference in this code between an internal and external - // io_service is the different constructor to init_asio + // io_context is the different constructor to init_asio ws_server.init_asio(&service); // Register our message handler @@ -87,6 +87,6 @@ int main() { // TODO: add a timer? - // Start the Asio io_service run loop for all + // Start the Asio io_context run loop for all service.run(); } \ No newline at end of file diff --git a/examples/external_io_service/tcp_echo_server.hpp b/examples/external_io_context/tcp_echo_server.hpp similarity index 95% rename from examples/external_io_service/tcp_echo_server.hpp rename to examples/external_io_context/tcp_echo_server.hpp index ef4ce1855..a1460bc05 100644 --- a/examples/external_io_service/tcp_echo_server.hpp +++ b/examples/external_io_context/tcp_echo_server.hpp @@ -44,7 +44,7 @@ namespace asio = websocketpp::lib::asio; struct tcp_echo_session : websocketpp::lib::enable_shared_from_this { typedef websocketpp::lib::shared_ptr ptr; - tcp_echo_session(asio::io_service & service) : m_socket(service) {} + tcp_echo_session(asio::io_context & service) : m_socket(service) {} void start() { m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)), @@ -72,7 +72,7 @@ struct tcp_echo_session : websocketpp::lib::enable_shared_from_thisget_payload() == "upgrade") { diff --git a/examples/iostream_server/iostream_server.cpp b/examples/iostream_server/iostream_server.cpp index 16d59c4b3..d070cccb6 100644 --- a/examples/iostream_server/iostream_server.cpp +++ b/examples/iostream_server/iostream_server.cpp @@ -16,7 +16,7 @@ using websocketpp::lib::error_code; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { s->get_alog().write(websocketpp::log::alevel::app, "Text Message Received: "+msg->get_payload()); diff --git a/examples/print_client/print_client.cpp b/examples/print_client/print_client.cpp index 7ba6e5fc9..20a2bd3e8 100644 --- a/examples/print_client/print_client.cpp +++ b/examples/print_client/print_client.cpp @@ -68,7 +68,7 @@ int main(int argc, char* argv[]) { // exchanged until the event loop starts running in the next line. c.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop // this will cause a single connection to be made to the server. c.run() // will exit when this connection is closed. c.run(); diff --git a/examples/print_client_tls/print_client_tls.cpp b/examples/print_client_tls/print_client_tls.cpp index 469d9c61e..43164eaf7 100644 --- a/examples/print_client_tls/print_client_tls.cpp +++ b/examples/print_client_tls/print_client_tls.cpp @@ -239,7 +239,7 @@ int main(int argc, char* argv[]) { c.get_alog().write(websocketpp::log::alevel::app, "Connecting to " + uri); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop // this will cause a single connection to be made to the server. c.run() // will exit when this connection is closed. c.run(); diff --git a/examples/scratch_client/scratch_client.cpp b/examples/scratch_client/scratch_client.cpp index a6a1fbcfe..1856fd42e 100644 --- a/examples/scratch_client/scratch_client.cpp +++ b/examples/scratch_client/scratch_client.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; diff --git a/examples/scratch_server/scratch_server.cpp b/examples/scratch_server/scratch_server.cpp index 901ae8e5d..820689056 100644 --- a/examples/scratch_server/scratch_server.cpp +++ b/examples/scratch_server/scratch_server.cpp @@ -53,7 +53,7 @@ typedef websocketpp::server server; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { /*std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message (" << msg->get_payload().size() << "): " << msg->get_payload() << std::endl; @@ -94,7 +94,7 @@ int main(int argc, char * argv[]) { // Start the server accept loop echo_server.start_accept(); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop echo_server.run(); } catch (websocketpp::exception const & e) { std::cout << e.what() << std::endl; diff --git a/examples/simple_broadcast_server/simple_broadcast_server.cpp b/examples/simple_broadcast_server/simple_broadcast_server.cpp index bdea98732..dcaa8a881 100644 --- a/examples/simple_broadcast_server/simple_broadcast_server.cpp +++ b/examples/simple_broadcast_server/simple_broadcast_server.cpp @@ -20,11 +20,11 @@ class broadcast_server { m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { m_connections.erase(hdl); } @@ -33,7 +33,7 @@ class broadcast_server { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { for (auto it : m_connections) { m_server.send(it,msg); } diff --git a/examples/sip_client/sip_client.cpp b/examples/sip_client/sip_client.cpp index 66fa85784..cd1d5308d 100644 --- a/examples/sip_client/sip_client.cpp +++ b/examples/sip_client/sip_client.cpp @@ -23,7 +23,7 @@ client sip_client; bool received; -void on_open(client* c, websocketpp::connection_hdl hdl) { +void on_open(client* c, websocketpp::connection_hdl_ref hdl) { // now it is safe to use the connection std::cout << "connection ready" << std::endl; @@ -33,7 +33,7 @@ void on_open(client* c, websocketpp::connection_hdl hdl) { sip_client.send(hdl, SIP_msg.c_str(), websocketpp::frame::opcode::text); } -void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(client* c, websocketpp::connection_hdl_ref hdl, message_ptr msg) { client::connection_ptr con = sip_client.get_con_from_hdl(hdl); std::cout << "Received a reply:" << std::endl; @@ -69,7 +69,7 @@ int main(int argc, char* argv[]) { sip_client.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop sip_client.run(); while(!received) { diff --git a/examples/subprotocol_server/subprotocol_server.cpp b/examples/subprotocol_server/subprotocol_server.cpp index c194d6dc0..9178d4822 100644 --- a/examples/subprotocol_server/subprotocol_server.cpp +++ b/examples/subprotocol_server/subprotocol_server.cpp @@ -12,7 +12,7 @@ using websocketpp::lib::bind; using websocketpp::lib::ref; -bool validate(server & s, connection_hdl hdl) { +session::validation::value validate(server & s, connection_hdl_ref hdl) { server::connection_ptr con = s.get_con_from_hdl(hdl); std::cout << "Cache-Control: " << con->get_request_header("Cache-Control") << std::endl; @@ -28,7 +28,7 @@ bool validate(server & s, connection_hdl hdl) { con->select_subprotocol(subp_requests[0]); } - return true; + return session::validation::accept; } int main() { diff --git a/examples/telemetry_client/telemetry_client.cpp b/examples/telemetry_client/telemetry_client.cpp index f0f7fae72..12390b821 100644 --- a/examples/telemetry_client/telemetry_client.cpp +++ b/examples/telemetry_client/telemetry_client.cpp @@ -62,10 +62,10 @@ class telemetry_client { m_hdl = con->get_handle(); // Queue the connection. No DNS queries or network connections will be - // made until the io_service event loop is run. + // made until the io_context event loop is run. m_client.connect(con); - // Create a thread to run the ASIO io_service event loop + // Create a thread to run the ASIO io_context event loop websocketpp::lib::thread asio_thread(&client::run, &m_client); // Create a thread to run the telemetry loop diff --git a/examples/telemetry_server/telemetry_server.cpp b/examples/telemetry_server/telemetry_server.cpp index 22c155edf..cd8cec857 100644 --- a/examples/telemetry_server/telemetry_server.cpp +++ b/examples/telemetry_server/telemetry_server.cpp @@ -69,7 +69,7 @@ class telemetry_server { // Set the initial timer to start telemetry set_timer(); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop try { m_endpoint.run(); } catch (websocketpp::exception const & e) { @@ -109,7 +109,7 @@ class telemetry_server { set_timer(); } - void on_http(connection_hdl hdl) { + void on_http(connection_hdl_ref hdl) { // Upgrade our connection handle to a full connection_ptr server::connection_ptr con = m_endpoint.get_con_from_hdl(hdl); @@ -156,11 +156,11 @@ class telemetry_server { con->set_status(websocketpp::http::status_code::ok); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { m_connections.erase(hdl); } diff --git a/examples/testee_client/testee_client.cpp b/examples/testee_client/testee_client.cpp index b66e63353..2295c87c6 100644 --- a/examples/testee_client/testee_client.cpp +++ b/examples/testee_client/testee_client.cpp @@ -80,7 +80,7 @@ typedef websocketpp::config::asio_client::message_type::ptr message_ptr; int case_count = 0; -void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(client* c, websocketpp::connection_hdl_ref hdl, message_ptr msg) { client::connection_ptr con = c->get_con_from_hdl(hdl); if (con->get_resource() == "/getCaseCount") { @@ -117,7 +117,7 @@ int main(int argc, char* argv[]) { client::connection_ptr con = c.get_connection(uri+"/getCaseCount", ec); c.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop c.run(); std::cout << "case count: " << case_count << std::endl; diff --git a/examples/testee_server/testee_server.cpp b/examples/testee_server/testee_server.cpp index cf876f7e1..09c6e100c 100644 --- a/examples/testee_server/testee_server.cpp +++ b/examples/testee_server/testee_server.cpp @@ -61,9 +61,9 @@ struct testee_config : public websocketpp::config::asio { typedef websocketpp::transport::asio::endpoint transport_type; - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; /// permessage_compress extension @@ -84,11 +84,11 @@ using websocketpp::lib::error_code; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } -void on_socket_init(websocketpp::connection_hdl, boost::asio::ip::tcp::socket & s) { +void on_socket_init(websocketpp::connection_hdl_ref, boost::asio::ip::tcp::socket & s) { boost::asio::ip::tcp::no_delay option(true); s.set_option(option); } @@ -131,7 +131,7 @@ int main(int argc, char * argv[]) { // Start the server accept loop testee_server.start_accept(&on_end_accept); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop if (num_threads == 1) { testee_server.run(); } else { diff --git a/examples/utility_client/utility_client.cpp b/examples/utility_client/utility_client.cpp index aa43b8e39..5c38d5235 100644 --- a/examples/utility_client/utility_client.cpp +++ b/examples/utility_client/utility_client.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; @@ -79,7 +79,7 @@ class connection_metadata { m_error_reason = s.str(); } - void on_message(websocketpp::connection_hdl, client::message_ptr msg) { + void on_message(websocketpp::connection_hdl_ref, client::message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { m_messages.push_back("<< " + msg->get_payload()); } else { diff --git a/roadmap.md b/roadmap.md index b35c54ad0..c6103322f 100644 --- a/roadmap.md +++ b/roadmap.md @@ -16,7 +16,7 @@ Complete & Tested: - open_handler - close_handler - echo_server & echo_server_tls -- External io_service support +- External io_context support - TLS support - exception/error handling - Timeouts diff --git a/test/connection/connection.cpp b/test/connection/connection.cpp index 4cb2aca57..bd080fb5c 100644 --- a/test/connection/connection.cpp +++ b/test/connection/connection.cpp @@ -127,8 +127,8 @@ struct debug_config_client : public websocketpp::config::core { typedef core::endpoint_base endpoint_base; typedef connection_extension connection_base; - static const websocketpp::log::level elog_level = websocketpp::log::elevel::none; - static const websocketpp::log::level alog_level = websocketpp::log::alevel::none; + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; }; struct connection_setup { @@ -147,21 +147,21 @@ struct connection_setup { typedef websocketpp::client debug_client; typedef websocketpp::server debug_server; -/*void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +/*void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); }*/ -void validate_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void validate_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } -bool validate_set_ua(server* s, websocketpp::connection_hdl hdl) { +session::validation::value validate_set_ua(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); con->replace_header("Server","foo"); - return true; + return session::validation::accept; } -void http_func(server* s, websocketpp::connection_hdl hdl) { +void http_func(server* s, websocketpp::connection_hdl_ref hdl) { using namespace websocketpp::http; server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -175,7 +175,7 @@ void http_func(server* s, websocketpp::connection_hdl hdl) { BOOST_CHECK_EQUAL(con->get_response_msg(), status_code::get_string(status_code::ok)); } -void http_func_with_move(server* s, websocketpp::connection_hdl hdl) { +void http_func_with_move(server* s, websocketpp::connection_hdl_ref hdl) { using namespace websocketpp::http; server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -189,7 +189,7 @@ void http_func_with_move(server* s, websocketpp::connection_hdl hdl) { BOOST_CHECK_EQUAL(con->get_response_msg(), status_code::get_string(status_code::ok)); } -void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl hdl) { +void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl_ref hdl) { *deferred = true; server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -199,7 +199,7 @@ void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl hdl } void check_on_fail(server* s, websocketpp::lib::error_code ec, bool & called, - websocketpp::connection_hdl hdl) + websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -207,17 +207,17 @@ void check_on_fail(server* s, websocketpp::lib::error_code ec, bool & called, called = true; } -void on_open_print(server* s, websocketpp::connection_hdl hdl) +void on_open_print(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); std::cout << con->get_uri() << std::endl; } -void fail_on_open(websocketpp::connection_hdl) { +void fail_on_open(websocketpp::connection_hdl_ref) { BOOST_CHECK(false); } -void fail_on_http(websocketpp::connection_hdl) { +void fail_on_http(websocketpp::connection_hdl_ref) { BOOST_CHECK(false); } diff --git a/test/connection/connection_tu2.cpp b/test/connection/connection_tu2.cpp index 540962486..bbc94abb1 100644 --- a/test/connection/connection_tu2.cpp +++ b/test/connection/connection_tu2.cpp @@ -27,7 +27,7 @@ #include "connection_tu2.hpp" -void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } diff --git a/test/connection/connection_tu2.hpp b/test/connection/connection_tu2.hpp index 8990ce00c..39e87431f 100644 --- a/test/connection/connection_tu2.hpp +++ b/test/connection/connection_tu2.hpp @@ -46,6 +46,6 @@ using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; -void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg); +void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg); std::string run_server_test(std::string input, bool log = false); std::string run_server_test(server & s, std::string input, bool log = false); diff --git a/test/endpoint/endpoint.cpp b/test/endpoint/endpoint.cpp index b4c429ebc..749624f23 100644 --- a/test/endpoint/endpoint.cpp +++ b/test/endpoint/endpoint.cpp @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE( initialize_server_asio ) { BOOST_AUTO_TEST_CASE( initialize_server_asio_external ) { websocketpp::server s; - boost::asio::io_service ios; + boost::asio::io_context ios; s.init_asio(&ios); } diff --git a/test/http/parser.cpp b/test/http/parser.cpp index fbe196668..cd8661f28 100644 --- a/test/http/parser.cpp +++ b/test/http/parser.cpp @@ -855,7 +855,7 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 159 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -878,7 +878,7 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response_trailing ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 159 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -901,7 +901,7 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response_trailing_large ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 159 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -923,7 +923,7 @@ BOOST_AUTO_TEST_CASE( response_with_non_standard_lws ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 158 ); - BOOST_CHECK( r.headers_ready() ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -945,7 +945,7 @@ BOOST_AUTO_TEST_CASE( plain_http_response ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 405 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK( r.ready() == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::ok ); @@ -975,7 +975,7 @@ BOOST_AUTO_TEST_CASE( parse_istream ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 405 ); - BOOST_CHECK_EQUAL( r.headers_ready(), true ); + BOOST_CHECK_EQUAL( r.has_received(websocketpp::http::parser::response::state::HEADERS), true ); BOOST_CHECK_EQUAL( r.ready(), true ); } diff --git a/test/roles/client.cpp b/test/roles/client.cpp index a58556249..b9d8ba2d0 100644 --- a/test/roles/client.cpp +++ b/test/roles/client.cpp @@ -57,8 +57,8 @@ struct stub_config : public websocketpp::config::core { typedef core::endpoint_base endpoint_base; - static const websocketpp::log::level elog_level = websocketpp::log::elevel::none; - static const websocketpp::log::level alog_level = websocketpp::log::alevel::none; + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; }; typedef websocketpp::client client; diff --git a/test/roles/server.cpp b/test/roles/server.cpp index b283570b9..f17bb08ec 100644 --- a/test/roles/server.cpp +++ b/test/roles/server.cpp @@ -87,12 +87,12 @@ std::string run_server_test(server& s, std::string input) { } /* handler library*/ -void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } -bool validate_func_subprotocol(server* s, std::string* out, std::string accept, - websocketpp::connection_hdl hdl) +session::validation::value validate_func_subprotocol(server* s, std::string* out, std::string accept, + websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -111,10 +111,10 @@ bool validate_func_subprotocol(server* s, std::string* out, std::string accept, con->select_subprotocol(accept); } - return true; + return session::validation::accept; } -void open_func_subprotocol(server* s, std::string* out, websocketpp::connection_hdl hdl) { +void open_func_subprotocol(server* s, std::string* out, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); *out = con->get_subprotocol(); @@ -289,13 +289,13 @@ struct test_config : public websocketpp::config::core { typedef test_transport::endpoint transport_type; }; -void handle_fail(websocketpp::server * s, websocketpp::lib::error_code * rec, websocketpp::connection_hdl hdl) { +void handle_fail(websocketpp::server * s, websocketpp::lib::error_code * rec, websocketpp::connection_hdl_ref hdl) { websocketpp::server::connection_ptr con = s->get_con_from_hdl(hdl); *rec = con->get_ec(); } -void handle_fail_count(websocketpp::server * s, websocketpp::lib::error_code rec, int * count, websocketpp::connection_hdl hdl) { +void handle_fail_count(websocketpp::server * s, websocketpp::lib::error_code rec, int * count, websocketpp::connection_hdl_ref hdl) { websocketpp::server::connection_ptr con = s->get_con_from_hdl(hdl); if (con->get_ec() == rec) { diff --git a/test/transport/asio/timers.cpp b/test/transport/asio/timers.cpp index aa03d6189..391b755ff 100644 --- a/test/transport/asio/timers.cpp +++ b/test/transport/asio/timers.cpp @@ -54,9 +54,9 @@ void run_dummy_server(int port) { using boost::asio::ip::tcp; try { - boost::asio::io_service io_service; - tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v6(), port)); - tcp::socket socket(io_service); + boost::asio::io_context io_context; + tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v6(), port)); + tcp::socket socket(io_context); acceptor.accept(socket); for (;;) { @@ -79,7 +79,7 @@ void run_dummy_server(int port) { // Wait for the specified time period then fail the test void run_test_timer(long value) { - boost::asio::io_service ios; + boost::asio::io_context ios; boost::asio::deadline_timer t(ios,boost::posix_time::milliseconds(value)); boost::system::error_code ec; t.wait(ec); diff --git a/test/transport/integration.cpp b/test/transport/integration.cpp index c083cfd01..ef4735255 100644 --- a/test/transport/integration.cpp +++ b/test/transport/integration.cpp @@ -69,13 +69,15 @@ struct config : public websocketpp::config::asio_client { typedef websocketpp::transport::asio::endpoint transport_type; - //static const websocketpp::log::level elog_level = websocketpp::log::elevel::all; - //static const websocketpp::log::level alog_level = websocketpp::log::alevel::all; + //static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all; + //static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all; /// Length of time before an opening handshake is aborted static const long timeout_open_handshake = 500; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 500; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 500; /// Length of time to wait for a pong after a ping static const long timeout_pong = 500; }; @@ -118,6 +120,8 @@ struct config_tls : public websocketpp::config::asio_tls_client { static const long timeout_open_handshake = 500; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 500; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 500; /// Length of time to wait for a pong after a ping static const long timeout_pong = 500; }; @@ -136,7 +140,7 @@ using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; template -void close_after_timeout(T & e, websocketpp::connection_hdl hdl, long timeout) { +void close_after_timeout(T & e, websocketpp::connection_hdl_ref hdl, long timeout) { sleep(timeout); websocketpp::lib::error_code ec; @@ -221,9 +225,9 @@ void run_dummy_server(int port) { using boost::asio::ip::tcp; try { - boost::asio::io_service io_service; - tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v6(), port)); - tcp::socket socket(io_service); + boost::asio::io_context io_context; + tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v6(), port)); + tcp::socket socket(io_context); acceptor.accept(socket); for (;;) { @@ -248,11 +252,11 @@ void run_dummy_client(std::string port) { using boost::asio::ip::tcp; try { - boost::asio::io_service io_service; - tcp::resolver resolver(io_service); + boost::asio::io_context io_context; + tcp::resolver resolver(io_context); tcp::resolver::query query("localhost", port); tcp::resolver::iterator iterator = resolver.resolve(query); - tcp::socket socket(io_service); + tcp::socket socket(io_context); boost::asio::connect(socket, iterator); for (;;) { @@ -273,16 +277,16 @@ void run_dummy_client(std::string port) { } } -bool on_ping(server * s, websocketpp::connection_hdl, std::string) { +bool on_ping(server * s, websocketpp::connection_hdl_ref, std::string) { s->get_alog().write(websocketpp::log::alevel::app,"got ping"); return false; } -void cancel_on_open(server * s, websocketpp::connection_hdl) { +void cancel_on_open(server * s, websocketpp::connection_hdl_ref) { s->stop_listening(); } -void stop_on_close(server * s, websocketpp::connection_hdl hdl) { +void stop_on_close(server * s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); //BOOST_CHECK_EQUAL( con->get_local_close_code(), websocketpp::close::status::normal ); //BOOST_CHECK_EQUAL( con->get_remote_close_code(), websocketpp::close::status::normal ); @@ -290,38 +294,38 @@ void stop_on_close(server * s, websocketpp::connection_hdl hdl) { } template -void ping_on_open(T * c, std::string payload, websocketpp::connection_hdl hdl) { +void ping_on_open(T * c, std::string payload, websocketpp::connection_hdl_ref hdl) { typename T::connection_ptr con = c->get_con_from_hdl(hdl); websocketpp::lib::error_code ec; con->ping(payload,ec); BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); } -void fail_on_pong(websocketpp::connection_hdl, std::string) { +void fail_on_pong(websocketpp::connection_hdl_ref, std::string) { BOOST_FAIL( "expected no pong handler" ); } -void fail_on_pong_timeout(websocketpp::connection_hdl, std::string) { +void fail_on_pong_timeout(websocketpp::connection_hdl_ref, std::string) { BOOST_FAIL( "expected no pong timeout" ); } -void req_pong(std::string expected_payload, websocketpp::connection_hdl, +void req_pong(std::string expected_payload, websocketpp::connection_hdl_ref, std::string payload) { BOOST_CHECK_EQUAL( expected_payload, payload ); } -void fail_on_open(websocketpp::connection_hdl) { +void fail_on_open(websocketpp::connection_hdl_ref) { BOOST_FAIL( "expected no open handler" ); } -void delay(websocketpp::connection_hdl, long duration) { +void delay(websocketpp::connection_hdl_ref, long duration) { sleep(duration); } template void check_ec(T * c, websocketpp::lib::error_code ec, - websocketpp::connection_hdl hdl) + websocketpp::connection_hdl_ref hdl) { typename T::connection_ptr con = c->get_con_from_hdl(hdl); BOOST_CHECK_EQUAL( con->get_ec(), ec ); @@ -331,7 +335,7 @@ void check_ec(T * c, websocketpp::lib::error_code ec, template void check_ec_and_stop(T * e, websocketpp::lib::error_code ec, - websocketpp::connection_hdl hdl) + websocketpp::connection_hdl_ref hdl) { typename T::connection_ptr con = e->get_con_from_hdl(hdl); BOOST_CHECK_EQUAL( con->get_ec(), ec ); @@ -342,7 +346,7 @@ void check_ec_and_stop(T * e, websocketpp::lib::error_code ec, template void req_pong_timeout(T * c, std::string expected_payload, - websocketpp::connection_hdl hdl, std::string payload) + websocketpp::connection_hdl_ref hdl, std::string payload) { typename T::connection_ptr con = c->get_con_from_hdl(hdl); BOOST_CHECK_EQUAL( payload, expected_payload ); @@ -350,7 +354,7 @@ void req_pong_timeout(T * c, std::string expected_payload, } template -void close(T * e, websocketpp::connection_hdl hdl) { +void close(T * e, websocketpp::connection_hdl_ref hdl) { e->get_con_from_hdl(hdl)->close(websocketpp::close::status::normal,""); } @@ -358,11 +362,11 @@ class test_deadline_timer { public: test_deadline_timer(int seconds) - : m_timer(m_io_service, boost::posix_time::seconds(seconds)) + : m_timer(m_io_context, boost::posix_time::seconds(seconds)) { m_timer.async_wait(bind(&test_deadline_timer::expired, this, ::_1)); - std::size_t (boost::asio::io_service::*run)() = &boost::asio::io_service::run; - m_timer_thread = websocketpp::lib::thread(websocketpp::lib::bind(run, &m_io_service)); + std::size_t (boost::asio::io_context::*run)() = &boost::asio::io_context::run; + m_timer_thread = websocketpp::lib::thread(websocketpp::lib::bind(run, &m_io_context)); } ~test_deadline_timer() { @@ -379,7 +383,7 @@ class test_deadline_timer BOOST_FAIL("Test timed out"); } - boost::asio::io_service m_io_service; + boost::asio::io_context m_io_context; boost::asio::deadline_timer m_timer; websocketpp::lib::thread m_timer_thread; }; @@ -541,7 +545,7 @@ BOOST_AUTO_TEST_CASE( client_runs_out_of_work ) { c.run(); - // This test checks that an io_service with no work ends immediately. + // This test checks that an io_context with no work ends immediately. BOOST_CHECK(true); } diff --git a/test/utility/uri.cpp b/test/utility/uri.cpp index f6d18726a..82d7bb7e3 100644 --- a/test/utility/uri.cpp +++ b/test/utility/uri.cpp @@ -40,10 +40,9 @@ BOOST_AUTO_TEST_CASE( uri_valid ) { websocketpp::uri uri("ws://localhost:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); - BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); + BOOST_CHECK_EQUAL( uri.get_host(), "localhost" ); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); BOOST_CHECK_EQUAL( uri.get_query(), "" ); @@ -161,9 +160,8 @@ BOOST_AUTO_TEST_CASE( uri_invalid_ipv6 ) { BOOST_AUTO_TEST_CASE( uri_valid_no_slash ) { websocketpp::uri uri("ws://localhost"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 80 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -172,9 +170,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_slash ) { BOOST_AUTO_TEST_CASE( uri_valid_no_slash_with_fragment ) { websocketpp::uri uri("ws://localhost#foo"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 80 ); BOOST_CHECK_EQUAL( uri.get_resource(), "#foo" ); @@ -184,9 +181,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_slash_with_fragment ) { BOOST_AUTO_TEST_CASE( uri_valid_no_port_unsecure ) { websocketpp::uri uri("ws://localhost/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 80 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -196,9 +192,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_port_unsecure ) { BOOST_AUTO_TEST_CASE( uri_valid_no_port_secure ) { websocketpp::uri uri("wss://localhost/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 443 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -208,9 +203,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_port_secure ) { BOOST_AUTO_TEST_CASE( uri_valid_no_resource ) { websocketpp::uri uri("wss://localhost:9000"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -220,9 +214,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_resource ) { BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal ) { websocketpp::uri uri("wss://[::1]:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "::1"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -235,9 +228,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal ) { BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal_default_port ) { websocketpp::uri uri("wss://[::1]/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "::1"); BOOST_CHECK_EQUAL( uri.get_port(), 443 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -250,9 +242,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal_default_port ) { BOOST_AUTO_TEST_CASE( uri_valid_2 ) { websocketpp::uri uri("wss://thor-websocket.zaphoyd.net:88/"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "thor-websocket.zaphoyd.net"); BOOST_CHECK_EQUAL( uri.get_port(), 88 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -277,9 +268,8 @@ BOOST_AUTO_TEST_CASE( uri_invalid_scheme ) { BOOST_AUTO_TEST_CASE( uri_http_scheme ) { websocketpp::uri uri("http://localhost:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "http"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::http ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -289,9 +279,8 @@ BOOST_AUTO_TEST_CASE( uri_http_scheme ) { BOOST_AUTO_TEST_CASE( uri_valid_ipv4_literal ) { websocketpp::uri uri("wss://127.0.0.1:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "127.0.0.1"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -301,9 +290,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_ipv4_literal ) { BOOST_AUTO_TEST_CASE( uri_valid_3 ) { websocketpp::uri uri("wss://localhost:9000/chat/foo/bar"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat/foo/bar" ); @@ -355,9 +343,8 @@ BOOST_AUTO_TEST_CASE( uri_invalid_free_delim ) { BOOST_AUTO_TEST_CASE( uri_valid_4 ) { websocketpp::uri uri("wss://localhost:9000/chat/foo/bar?foo=bar"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss" ); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat/foo/bar?foo=bar" ); @@ -368,9 +355,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_4 ) { BOOST_AUTO_TEST_CASE( uri_valid_v4_mapped ) { websocketpp::uri uri("wss://[0000:0000:0000:0000:0000:0000:192.168.1.1]:9000/"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss" ); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "0000:0000:0000:0000:0000:0000:192.168.1.1"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -380,9 +366,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_v4_mapped ) { BOOST_AUTO_TEST_CASE( uri_valid_v6_mixed_case ) { websocketpp::uri uri("wss://[::10aB]:9000/"); - BOOST_CHECK( uri.get_valid() == true ); - BOOST_CHECK( uri.get_secure() == true ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss" ); + BOOST_CHECK( uri.get_secure() ); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "::10aB"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); diff --git a/tutorials/utility_client/step4.cpp b/tutorials/utility_client/step4.cpp index 73aee65a8..12fdbbca2 100644 --- a/tutorials/utility_client/step4.cpp +++ b/tutorials/utility_client/step4.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); diff --git a/tutorials/utility_client/step5.cpp b/tutorials/utility_client/step5.cpp index 3e57b3c24..eab8f704f 100644 --- a/tutorials/utility_client/step5.cpp +++ b/tutorials/utility_client/step5.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; diff --git a/tutorials/utility_client/step6.cpp b/tutorials/utility_client/step6.cpp index 208b95db6..8ec1aee9e 100644 --- a/tutorials/utility_client/step6.cpp +++ b/tutorials/utility_client/step6.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; @@ -79,7 +79,7 @@ class connection_metadata { m_error_reason = s.str(); } - void on_message(websocketpp::connection_hdl, client::message_ptr msg) { + void on_message(websocketpp::connection_hdl_ref, client::message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { m_messages.push_back("<< " + msg->get_payload()); } else { diff --git a/tutorials/utility_client/utility_client.md b/tutorials/utility_client/utility_client.md index 696113df8..c1b097211 100644 --- a/tutorials/utility_client/utility_client.md +++ b/tutorials/utility_client/utility_client.md @@ -380,7 +380,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -388,14 +388,14 @@ public: , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -554,7 +554,7 @@ During the close handler call WebSocket++ connections offer the following method The `connection_metadata::on_close` method is added. This method retrieves the close code and reason from the closing handshake and stores it in the local error reason field. ~~~{.cpp} -void on_close(client * c, websocketpp::connection_hdl hdl) { +void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; @@ -678,13 +678,13 @@ This step adds a command to send a message on a given connection and updates the Messages are sent using `endpoint::send`. This is a thread safe method that may be called from anywhere to queue a message for sending on the specified connection. There are three send overloads for use with different scenarios. -Each method takes a `connection_hdl` to indicate which connection to send the message on as well as a `frame::opcode::value` to indicate which opcode to label the message as. All overloads are also available with an exception free varient that fills in a a status/error code instead of throwing. +Each method takes a `connection_hdl_ref` to indicate which connection to send the message on as well as a `frame::opcode::value` to indicate which opcode to label the message as. All overloads are also available with an exception free varient that fills in a a status/error code instead of throwing. -The first overload, `connection_hdl hdl, std::string const & payload, frame::opcode::value op`, takes a `std::string`. The string contents are copied into an internal buffer and can be safely modified after calling send. +The first overload, `connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op`, takes a `std::string`. The string contents are copied into an internal buffer and can be safely modified after calling send. -The second overload, `connection_hdl hdl, void const * payload, size_t len, frame::opcode::value op`, takes a void * buffer and length. The buffer contents are copied and can be safely modified after calling send. +The second overload, `connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op`, takes a void * buffer and length. The buffer contents are copied and can be safely modified after calling send. -The third overload, `connection_hdl hdl, message_ptr msg`, takes a WebSocket++ `message_ptr`. This overload allows a message to be constructed in place before the call to send. It also may allow a single message buffer to be sent multiple times, including to multiple connections, without copying. Whether or not this actually happens depends on other factors such as whether compression is enabled. The contents of the message buffer may not be safely modified after being sent. +The third overload, `connection_hdl_ref hdl, message_ptr msg`, takes a WebSocket++ `message_ptr`. This overload allows a message to be constructed in place before the call to send. It also may allow a single message buffer to be sent multiple times, including to multiple connections, without copying. Whether or not this actually happens depends on other factors such as whether compression is enabled. The contents of the message buffer may not be safely modified after being sent. > ###### Terminology: Outgoing WebSocket message queueing & flow control > In many configurations, such as when the Asio based transport is in use, WebSocket++ is an asynchronous system. As such the `endpoint::send` method may return before any bytes are actually written to the outgoing socket. In cases where send is called multiple times in quick succession messages may be coalesced and sent in the same operation or even the same TCP packet. When this happens the message boundaries are preserved (each call to send will produce a separate message). @@ -759,14 +759,14 @@ for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) { #### Receiving Messages -Messages are received by registering a message handler. This handler will be called once per message received and its signature is `void on_message(websocketpp::connection_hdl hdl, endpoint::message_ptr msg)`. The `connection_hdl`, like the similar parameter from the other handlers is a handle for the connection that the message was received on. The `message_ptr` is a pointer to an object that can be queried for the message payload, opcode, and other metadata. Note that the message_ptr type, as well as its underlying message type, is dependent on how your endpoint is configured and may be different for different configs. +Messages are received by registering a message handler. This handler will be called once per message received and its signature is `void on_message(websocketpp::connection_hdl_ref hdl, endpoint::message_ptr msg)`. The `connection_hdl_ref`, like the similar parameter from the other handlers is a handle for the connection that the message was received on. The `message_ptr` is a pointer to an object that can be queried for the message payload, opcode, and other metadata. Note that the message_ptr type, as well as its underlying message type, is dependent on how your endpoint is configured and may be different for different configs. #### Add a message handler to method to `connection_metadata` The message receiving behave that we are implementing will be to collect all messages sent and received and to print them in order when the show connection command is run. The sent messages are already being added to that list. Now we add a message handler that pushes received messages to the list as well. Text messages are pushed as-is. Binary messages are first converted to printable hexadecimal format. ~~~{.cpp} -void on_message(websocketpp::connection_hdl hdl, client::message_ptr msg) { +void on_message(websocketpp::connection_hdl_ref hdl, client::message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { m_messages.push_back(msg->get_payload()); } else { diff --git a/tutorials/utility_server/step1.cpp b/tutorials/utility_server/step1.cpp index c0e464382..dae4a2ab9 100644 --- a/tutorials/utility_server/step1.cpp +++ b/tutorials/utility_server/step1.cpp @@ -57,7 +57,7 @@ class utility_server { // Queues a connection accept operation m_endpoint.start_accept(); - // Start the Asio io_service run loop + // Start the Asio io_context run loop m_endpoint.run(); } private: diff --git a/tutorials/utility_server/step2.cpp b/tutorials/utility_server/step2.cpp index a2815bbb9..cde153864 100644 --- a/tutorials/utility_server/step2.cpp +++ b/tutorials/utility_server/step2.cpp @@ -56,7 +56,7 @@ class utility_server { )); } - void echo_handler(websocketpp::connection_hdl hdl, server::message_ptr msg) { + void echo_handler(websocketpp::connection_hdl_ref hdl, server::message_ptr msg) { // write a new message m_endpoint.send(hdl, msg->get_payload(), msg->get_opcode()); } @@ -68,7 +68,7 @@ class utility_server { // Queues a connection accept operation m_endpoint.start_accept(); - // Start the Asio io_service run loop + // Start the Asio io_context run loop m_endpoint.run(); } private: diff --git a/tutorials/utility_server/utility_server.md b/tutorials/utility_server/utility_server.md index 1c7ee3fc1..461eed071 100644 --- a/tutorials/utility_server/utility_server.md +++ b/tutorials/utility_server/utility_server.md @@ -56,7 +56,7 @@ m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log: Next, we initialize the transport system underlying the endpoint. This method is specific to the Asio transport not WebSocket++ core. It will not be necessary or present in endpoints that use a non-asio config. -> **Note:** This example uses an internal Asio `io_service` that is managed by the endpoint itself. This is a simple arrangement suitable for programs where WebSocket++ is the only code using Asio. If you have an existing program that already manages an `io_service` object or want to build a new program where WebSocket++ handlers share an io_service with other handlers you can pass the `io_service` you want WebSocket++ to register its handlers on to the `init_asio()` method and it will use it instead of generating and managing its own. [TODO: FAQ link instead?] +> **Note:** This example uses an internal Asio `io_context` that is managed by the endpoint itself. This is a simple arrangement suitable for programs where WebSocket++ is the only code using Asio. If you have an existing program that already manages an `io_context` object or want to build a new program where WebSocket++ handlers share an io_context with other handlers you can pass the `io_context` you want WebSocket++ to register its handlers on to the `init_asio()` method and it will use it instead of generating and managing its own. [TODO: FAQ link instead?] ~~~{.cpp} m_endpoint.init_asio(); @@ -64,7 +64,7 @@ m_endpoint.init_asio(); #### `utility_server::run` method -In addition to the constructor, we also add a run method that sets up the listening socket, begins accepting connections, starts the Asio io_service event loop. +In addition to the constructor, we also add a run method that sets up the listening socket, begins accepting connections, starts the Asio io_context event loop. ~~~{.cpp} // Listen on port 9002 @@ -73,7 +73,7 @@ m_endpoint.listen(9002); // Queues a connection accept operation m_endpoint.start_accept(); -// Start the Asio io_service run loop +// Start the Asio io_context run loop m_endpoint.run(); ~~~ @@ -123,7 +123,7 @@ public: // Queues a connection accept operation m_endpoint.start_accept(); - // Start the Asio io_service run loop + // Start the Asio io_context run loop m_endpoint.run(); } private: diff --git a/websocketpp/CMakeLists.txt b/websocketpp/CMakeLists.txt deleted file mode 100644 index 3ea8cc1c4..000000000 --- a/websocketpp/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -init_target("websocketpp") -final_target () diff --git a/websocketpp/common/connection_hdl.hpp b/websocketpp/common/connection_hdl.hpp index 1044c88e3..51e27a27a 100644 --- a/websocketpp/common/connection_hdl.hpp +++ b/websocketpp/common/connection_hdl.hpp @@ -46,6 +46,7 @@ namespace websocketpp { * that owns the handler. */ typedef lib::weak_ptr connection_hdl; +typedef const connection_hdl& connection_hdl_ref; } // namespace websocketpp diff --git a/websocketpp/common/md5.hpp b/websocketpp/common/md5.hpp index 81a676813..9899e1688 100644 --- a/websocketpp/common/md5.hpp +++ b/websocketpp/common/md5.hpp @@ -198,7 +198,7 @@ static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) { * On little-endian machines, we can process properly aligned * data without copying it. */ - if (!((data - (md5_byte_t const *)0) & 3)) { + if ((uintptr_t)((const void*)data) % 4 == 0) { /* data are properly aligned */ X = (md5_word_t const *)data; } else { diff --git a/websocketpp/common/network.hpp b/websocketpp/common/network.hpp index 26a4cb9ea..4bd838349 100644 --- a/websocketpp/common/network.hpp +++ b/websocketpp/common/network.hpp @@ -37,21 +37,12 @@ #endif #include +#include namespace websocketpp { namespace lib { namespace net { -inline bool is_little_endian() { - short int val = 0x1; - char *ptr = reinterpret_cast(&val); - return (ptr[0] == 1); -} - -#define TYP_INIT 0 -#define TYP_SMLE 1 -#define TYP_BIGE 2 - /// Convert 64 bit value to network byte order /** * This method is prefixed to avoid conflicts with operating system level @@ -64,18 +55,14 @@ inline bool is_little_endian() { * @return src converted to network byte order */ inline uint64_t _htonll(uint64_t src) { - static int typ = TYP_INIT; - unsigned char c; + if constexpr (::std::endian::native == ::std::endian::big) + return src; + union { uint64_t ull; unsigned char c[8]; } x; - if (typ == TYP_INIT) { - x.ull = 0x01; - typ = (x.c[7] == 0x01ULL) ? TYP_BIGE : TYP_SMLE; - } - if (typ == TYP_BIGE) - return src; + unsigned char c; x.ull = src; c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp index 93981aa0e..6fa12bef4 100644 --- a/websocketpp/config/core.hpp +++ b/websocketpp/config/core.hpp @@ -152,6 +152,8 @@ struct core { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; @@ -173,7 +175,7 @@ struct core { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; /// Default static access logging channels @@ -186,7 +188,7 @@ struct core { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; /// Size of the per-connection read buffer diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp index dadf8a4e7..011c5b81c 100644 --- a/websocketpp/config/core_client.hpp +++ b/websocketpp/config/core_client.hpp @@ -160,6 +160,8 @@ struct core_client { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; @@ -181,7 +183,7 @@ struct core_client { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; /// Default static access logging channels @@ -194,7 +196,7 @@ struct core_client { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; /// diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp index 223f72fbb..d25328b1c 100644 --- a/websocketpp/config/debug.hpp +++ b/websocketpp/config/debug.hpp @@ -152,6 +152,8 @@ struct debug_core { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; @@ -173,7 +175,7 @@ struct debug_core { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all; /// Default static access logging channels @@ -186,7 +188,7 @@ struct debug_core { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all; /// diff --git a/websocketpp/config/minimal_server.hpp b/websocketpp/config/minimal_server.hpp index dd1aedb9d..a6f872448 100644 --- a/websocketpp/config/minimal_server.hpp +++ b/websocketpp/config/minimal_server.hpp @@ -178,6 +178,8 @@ struct minimal_server { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; @@ -199,7 +201,7 @@ struct minimal_server { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; /// Default static access logging channels @@ -212,7 +214,7 @@ struct minimal_server { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; /// diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index f1e311ea2..51f103948 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,17 @@ namespace websocketpp { +namespace session{ + namespace validation { + // validation types supported by the validate handler + enum value { + reject = 0, + accept = 1, + defer = 2 + }; + } // namespace http_state +} // namespace session + /// The type and function signature of an open handler /** * The open handler is called once for every successful WebSocket connection @@ -56,7 +68,7 @@ namespace websocketpp { * upgrade the connection to the WebSocket protocol will trigger the http * handler instead of fail/open. */ -typedef lib::function open_handler; +typedef lib::function open_handler; /// The type and function signature of a close handler /** @@ -66,7 +78,7 @@ typedef lib::function open_handler; * The close handler will be called exactly once for every connection for which * the open handler was called. */ -typedef lib::function close_handler; +typedef lib::function close_handler; /// The type and function signature of a fail handler /** @@ -76,7 +88,7 @@ typedef lib::function close_handler; * upgrade the connection to the WebSocket protocol will trigger the http * handler instead of fail/open. */ -typedef lib::function fail_handler; +typedef lib::function fail_handler; /// The type and function signature of an interrupt handler /** @@ -88,7 +100,7 @@ typedef lib::function fail_handler; * This is typically used by another application thread to schedule some tasks * that can only be run from within the handler chain for thread safety reasons. */ -typedef lib::function interrupt_handler; +typedef lib::function interrupt_handler; /// The type and function signature of a ping handler /** @@ -98,7 +110,7 @@ typedef lib::function interrupt_handler; * true if a pong response should be sent, false if the pong response should be * suppressed. */ -typedef lib::function ping_handler; +typedef lib::function ping_handler; /// The type and function signature of a pong handler /** @@ -106,14 +118,21 @@ typedef lib::function ping_handler; * control frame. The string argument contains the pong payload. The payload is * a binary string up to 126 bytes in length. */ -typedef lib::function pong_handler; +typedef lib::function pong_handler; /// The type and function signature of a pong timeout handler /** * The pong timeout handler is called when a ping goes unanswered by a pong for * longer than the locally specified timeout period. */ -typedef lib::function pong_timeout_handler; +typedef lib::function pong_timeout_handler; + +/// The type and function signature of a progress handler +/** + * The progress handler is called when bytes of the HTTP message body are received (for plain HTTP requests) + * The two size parameters passed on are the completed and total size of the HTTP response, respectively. + */ +typedef lib::function progress_handler; /// The type and function signature of a validate handler /** @@ -126,7 +145,7 @@ typedef lib::function pong_timeout_handler; * should be accepted. Additional methods may be called during the function to * set response headers, set HTTP return/error codes, etc. */ -typedef lib::function validate_handler; +typedef lib::function validate_handler; /// The type and function signature of a http handler /** @@ -148,7 +167,7 @@ typedef lib::function validate_handler; * handlers may override the response status code to deliver any type of * response. */ -typedef lib::function http_handler; +typedef lib::function http_handler; // typedef lib::function read_handler; @@ -230,6 +249,20 @@ namespace http_state { } // namespace session +namespace http{ +struct body_options{ + // The encoding of the body that was given to connection::set_body() + std::optional input_encoding = {}; + + // The desired transfer encodings to consider when writing the body + std::vector transfer_encoding = {}; + + // The encodings to consider for body compression, if client indicated they support them via Accept-Encoding. + // Specify in order of preference (first is best). + std::vector output_encoding = {}; +}; +} + /// Represents an individual WebSocket connection template class connection @@ -309,8 +342,10 @@ class connection lib::placeholders::_1 )) , m_user_agent(ua) + , m_max_redirects(0) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_http_response_timeout_dur(config::timeout_read_http_response) , m_pong_timeout_dur(config::timeout_pong) , m_max_message_size(config::max_message_size) , m_state(session::state::connecting) @@ -474,6 +509,16 @@ class connection m_message_handler = h; } + /// Set progress handler + /** + * The progress handler is called when bytes making up a HTTP body are processed + * + * @param h The new progress_handler + */ + void set_progress_handler(progress_handler h) { + m_progress_handler = h; + } + ////////////////////////////////////////// // Connection timeouts and other limits // ////////////////////////////////////////// @@ -526,6 +571,33 @@ class connection m_close_handshake_timeout_dur = dur; } + /// Set http response timeout + /** + * Sets the length of time the library will wait for the response to be read + * before cancelling the request. This can be used to guard from http + * responses which are much larger than you anticipate (eg. files) or when + * the responder takes too long to write the body of the response. + * Timer starts when the request has been fully sent. + * + * Connections that time out will have their http handlers called with the + * http_read_response_timeout error code stored in the connection (use get_ec). + * + * The default value is specified via the compile time config value + * 'timeout_http_read_response'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @since 0.8.4 + * + * @param dur The length of the http response timeout in ms + */ + void set_http_response_timeout(long dur) { + m_http_response_timeout_dur = dur; + } + /// Set pong timeout /** * Sets the length of time the library will wait for a pong response to a @@ -578,6 +650,30 @@ class connection m_processor->set_max_message_size(new_value); } } + + /// Get maximum number of redirects + /** + * Get maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + */ + size_t get_max_redirects() const { + return m_max_redirects; + } + + /// Set maximum number of redirects + /** + * Set maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + * + * @param new_value The value to set as the max number of redirects to. + */ + void set_max_redirects(size_t new_value) { + m_max_redirects = new_value; + } /// Get maximum HTTP message body size /** @@ -609,6 +705,7 @@ class connection */ void set_max_http_body_size(size_t new_value) { m_request.set_max_body_size(new_value); + m_response.set_max_body_size(new_value); } ////////////////////////////////// @@ -777,6 +874,11 @@ class connection /// exception free variant of pong void pong(std::string const & payload, lib::error_code & ec); +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + // exception variant of close + void close(close::status::value const code, std::string const & reason); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + /// Close the connection /** * Initiates the close handshake process. @@ -797,9 +899,6 @@ class connection * @param code The close code to send * @param reason The close reason to send */ - void close(close::status::value const code, std::string const & reason); - - /// exception free variant of close void close(close::status::value const code, std::string const & reason, lib::error_code & ec); @@ -993,6 +1092,10 @@ class connection std::string const & get_response_msg() const { return m_response.get_status_msg(); } + + void consume_response_body() { + m_response.consume_body(); + } /// Set response status code and message (exception free) /** @@ -1031,9 +1134,8 @@ class connection * @see websocketpp::http::status_code::value (list of valid codes) */ void set_status(http::status_code::value code); -#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - - /// Set response status code and message (exception free) + + /// Set response status code and message (exception) /** * Sets the response status code and message to independent custom values. * use set_status(status_code::value) to set the code and have the standard @@ -1042,18 +1144,16 @@ class connection * This member function is valid only from the http() and validate() handler * callbacks. * - * @since 0.9.0 - * * @param[in] code Code to set * @param[in] msg Message to set - * @param[out] ec A status code describing the outcome of the operation - * @see websocketpp::http::response::set_status() + * @throw websocketpp::exception + * @see websocketpp::http::parser::response::set_status() * @see websocketpp::http::status_code::value (list of valid codes) */ - void set_status(http::status_code::value code, std::string const & msg, - lib::error_code & ec); + void set_status(http::status_code::value code, std::string const & msg); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Set response status code and message (exception) + /// Set response status code and message (exception free) /** * Sets the response status code and message to independent custom values. * use set_status(status_code::value) to set the code and have the standard @@ -1062,13 +1162,16 @@ class connection * This member function is valid only from the http() and validate() handler * callbacks. * + * @since 0.9.0 + * * @param[in] code Code to set * @param[in] msg Message to set - * @throw websocketpp::exception - * @see websocketpp::http::parser::response::set_status() + * @param[out] ec A status code describing the outcome of the operation + * @see websocketpp::http::response::set_status() * @see websocketpp::http::status_code::value (list of valid codes) */ - void set_status(http::status_code::value code, std::string const & msg); + void set_status(http::status_code::value code, std::string const & msg, + lib::error_code & ec); /// Set response body content (exception free) /** @@ -1087,7 +1190,7 @@ class connection * @see websocketpp::http::response::set_body * @see set_body(std::string const &) (exception version) */ - void set_body(std::string const & value, lib::error_code & ec); + void set_body(std::string const & value, const http::body_options& opts, lib::error_code & ec); #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Set response body content (exception) @@ -1106,17 +1209,19 @@ class connection * @see set_body(std::string const &, lib::error_code &) * (exception free version) */ - void set_body(std::string const & value); + void set_body(std::string const & value, const http::body_options& opts); + #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ - /// @copydoc websocketpp::connection::set_body(std::string const &, lib::error_code &) - void set_body(std::string && value, lib::error_code & ec); - #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// @copydoc websocketpp::connection::set_body(std::string const &) - void set_body(std::string && value); -#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + void set_body(std::string && value, const http::body_options& opts); +#endif + + /// @copydoc websocketpp::connection::set_body(std::string const &, lib::error_code &) + void set_body(std::string && value, const http::body_options& opts, lib::error_code & ec); + #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ /// Append a header (exception free) @@ -1143,6 +1248,7 @@ class connection void append_header(std::string const & key, std::string const & val, lib::error_code & ec); +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Append a header (exception) /** * Set the value of a header in the handshake HTTP request or response. If @@ -1163,6 +1269,7 @@ class connection * lib::error_code &) (exception free version) */ void append_header(std::string const & key, std::string const & val); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Replace a header (exception free) /** @@ -1253,6 +1360,23 @@ class connection request_type const & get_request() const { return m_request; } + + /// Set request object + /** + * Direct access to setting the request object. + * This can be used to set the request that the should be sent when using + * the connection as a HTTP request instead of a WS initiator. + * Do not use this in WS connections! + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.8.4 + * + * @param req the request object to use + */ + void set_request(request_type req, lib::error_code& ec); /// Get response object /** @@ -1272,6 +1396,31 @@ class connection response_type const & get_response() const { return m_response; } + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Get response object by using std::move + /** + * Direct access to the HTTP response sent or received as a part of the + * opening handshake. This can be used to call methods of the response + * object that are not part of the standard request API that connection + * wraps. + * + * IMPORTANT: The response in this connection object is invalid for use + * after this operation. Intended only to be used on a throw-away + * single-use HTTP request connection to prevent copying the response. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.8.4 + * + * @return A const reference to the raw response object + */ + response_type take_response() { + return std::move(m_response); + } +#endif /// Defer HTTP Response until later (Exception free) /** @@ -1452,6 +1601,7 @@ class connection void handle_open_handshake_timeout(lib::error_code const & ec); void handle_close_handshake_timeout(lib::error_code const & ec); + void handle_read_response_timeout(lib::error_code const & ec); void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred); void read_frame(); @@ -1504,10 +1654,16 @@ class connection * @param hdl A connection_hdl that the connection will use to refer * to itself. */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_connection_hdl = hdl; transport_con_type::set_handle(hdl); } + + /// Accept or reject a websocket connection request that was previouslly deferred by the validate handler + /** + * @param accept True to accept the websocket connection, false to reject it. + */ + lib::error_code deferred_accept(bool accept); protected: void handle_transport_init(lib::error_code const & ec); @@ -1517,9 +1673,13 @@ class connection /// Perform WebSocket handshake validation of m_request using m_processor. /// set m_response and return an error code indicating status. - lib::error_code process_handshake_request(); + session::validation::value process_handshake_request(lib::error_code & ec); + + lib::error_code finalize_handshake_response(bool accept); private: - + + /// Server-side check for valid content-encoding when replying to a request + void check_body_encoding(std::string & body, const http::body_options& opts, lib::error_code & ec); /// Completes m_response, serializes it, and sends it out on the wire. void write_http_response(lib::error_code const & ec); @@ -1647,6 +1807,9 @@ class connection // static settings std::string const m_user_agent; + // dynamic settings (per-connection) + size_t m_max_redirects; + /// Pointer to the connection handle connection_hdl m_connection_hdl; @@ -1661,10 +1824,12 @@ class connection http_handler m_http_handler; validate_handler m_validate_handler; message_handler m_message_handler; + progress_handler m_progress_handler; /// constant values long m_open_handshake_timeout_dur; long m_close_handshake_timeout_dur; + long m_http_response_timeout_dur; long m_pong_timeout_dur; size_t m_max_message_size; @@ -1699,7 +1864,7 @@ class connection /// @todo this is not memory efficient. this value is not used after the /// handshake. - std::string m_handshake_buffer; + std::string m_http_message_buffer; /// Pointer to the processor object for this connection /** diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index 10f525689..1f1195d17 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -32,6 +32,7 @@ #include +#include #include #include @@ -91,14 +92,16 @@ class endpoint : public config::transport_type, public config::endpoint_base { //friend connection; explicit endpoint(bool p_is_server) - : m_alog(new alog_type(config::alog_level, log::channel_type_hint::access)) - , m_elog(new elog_type(config::elog_level, log::channel_type_hint::error)) + : m_alog(lib::make_shared(config::alog_level, log::channel_type_hint::access)) + , m_elog(lib::make_shared(config::elog_level, log::channel_type_hint::error)) , m_user_agent(::websocketpp::user_agent) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_http_read_timeout_dur(config::timeout_read_http_response) , m_pong_timeout_dur(config::timeout_pong) , m_max_message_size(config::max_message_size) , m_max_http_body_size(config::max_http_body_size) + , m_max_redirects(0) , m_is_server(p_is_server) { m_alog->set_channels(config::alog_level); @@ -111,7 +114,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { /// Destructor - ~endpoint() {} + virtual ~endpoint() {} #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ // no copy constructor because endpoints are not copyable @@ -143,6 +146,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { , m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur) , m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur) + , m_http_read_timeout_dur(o.m_http_read_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) @@ -381,6 +385,33 @@ class endpoint : public config::transport_type, public config::endpoint_base { m_close_handshake_timeout_dur = dur; } + /// Set http body read timeout + /** + * Sets the length of time the library will wait after the HTTP body begins + * to be read before cancelling it. This can be used to guard from http + * responses which are much larger than you anticipate (eg. files) or when + * the responder takes too long to write the body of the response. + * + * Connections that time out will have their http handlers called with the + * http_read_response_timeout error code stored in the connection (use get_ec). + * + * The default value is specified via the compile time config value + * 'timeout_close_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @since 0.8.4 + * + * @param dur The length of the http body read timeout in ms + */ + void set_http_response_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_http_read_timeout_dur = dur; + } + /// Set pong timeout /** * Sets the length of time the library will wait for a pong response to a @@ -435,6 +466,30 @@ class endpoint : public config::transport_type, public config::endpoint_base { m_max_message_size = new_value; } + /// Get maximum number of redirects + /** + * Get maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + */ + size_t get_max_redirects() const { + return m_max_redirects; + } + + /// Set maximum number of redirects + /** + * Set maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + * + * @param new_value The value to set as the max number of redirects to. + */ + void set_max_redirects(size_t new_value) { + m_max_redirects = new_value; + } + /// Get maximum HTTP message body size /** * Get maximum HTTP message body size. Maximum message body size determines @@ -481,8 +536,8 @@ class endpoint : public config::transport_type, public config::endpoint_base { * default and an exception free variant. */ - void interrupt(connection_hdl hdl, lib::error_code & ec); - void interrupt(connection_hdl hdl); + void interrupt(connection_hdl_ref hdl, lib::error_code & ec); + void interrupt(connection_hdl_ref hdl); /// Pause reading of new data (exception free) /** @@ -504,10 +559,10 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * If reading is paused for this connection already nothing is changed. */ - void pause_reading(connection_hdl hdl, lib::error_code & ec); + void pause_reading(connection_hdl_ref hdl, lib::error_code & ec); /// Pause reading of new data - void pause_reading(connection_hdl hdl); + void pause_reading(connection_hdl_ref hdl); /// Resume reading of new data (exception free) /** @@ -516,10 +571,10 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * If reading is not paused for this connection already nothing is changed. */ - void resume_reading(connection_hdl hdl, lib::error_code & ec); + void resume_reading(connection_hdl_ref hdl, lib::error_code & ec); /// Resume reading of new data - void resume_reading(connection_hdl hdl); + void resume_reading(connection_hdl_ref hdl); /// Send deferred HTTP Response /** @@ -534,7 +589,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @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); + void send_http_response(connection_hdl_ref hdl, lib::error_code & ec); /// Send deferred HTTP Response (exception free) /** @@ -548,7 +603,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * @param hdl The connection to send the response on */ - void send_http_response(connection_hdl hdl); + void send_http_response(connection_hdl_ref hdl); /// Create a message and add it to the outgoing send queue (exception free) /** @@ -559,7 +614,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] op The opcode to generated the message with. * @param [out] ec A code to fill in for errors */ - void send(connection_hdl hdl, std::string const & payload, + void send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op, lib::error_code & ec); /// Create a message and add it to the outgoing send queue /** @@ -569,21 +624,23 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] payload The payload string to generated the message with * @param [in] op The opcode to generated the message with. */ - void send(connection_hdl hdl, std::string const & payload, + void send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op); - void send(connection_hdl hdl, void const * payload, size_t len, + void send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op, lib::error_code & ec); - void send(connection_hdl hdl, void const * payload, size_t len, + void send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op); - void send(connection_hdl hdl, message_ptr msg, lib::error_code & ec); - void send(connection_hdl hdl, message_ptr msg); + void send(connection_hdl_ref hdl, message_ptr msg, lib::error_code & ec); + void send(connection_hdl_ref hdl, message_ptr msg); - void close(connection_hdl hdl, close::status::value const code, + void close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason, lib::error_code & ec); - void close(connection_hdl hdl, close::status::value const code, +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + void close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Send a ping to a specific connection /** @@ -593,7 +650,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] payload The payload string to send. * @param [out] ec A reference to an error code to fill in */ - void ping(connection_hdl hdl, std::string const & payload, + void ping(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec); /// Send a ping to a specific connection /** @@ -604,7 +661,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] hdl The connection_hdl of the connection to send to. * @param [in] payload The payload string to send. */ - void ping(connection_hdl hdl, std::string const & payload); + void ping(connection_hdl_ref hdl, std::string const & payload); /// Send a pong to a specific connection /** @@ -614,7 +671,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] payload The payload string to send. * @param [out] ec A reference to an error code to fill in */ - void pong(connection_hdl hdl, std::string const & payload, + void pong(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec); /// Send a pong to a specific connection /** @@ -625,7 +682,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] hdl The connection_hdl of the connection to send to. * @param [in] payload The payload string to send. */ - void pong(connection_hdl hdl, std::string const & payload); + void pong(connection_hdl_ref hdl, std::string const & payload); /// Retrieves a connection_ptr from a connection_hdl (exception free) /** @@ -641,7 +698,7 @@ 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) { + connection_ptr get_con_from_hdl(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = lib::static_pointer_cast( hdl.lock()); if (!con) { @@ -652,7 +709,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Retrieves a connection_ptr from a connection_hdl (exception version) - connection_ptr get_con_from_hdl(connection_hdl hdl) { + connection_ptr get_con_from_hdl(connection_hdl_ref hdl) { lib::error_code ec; connection_ptr con = this->get_con_from_hdl(hdl,ec); if (ec) { @@ -683,9 +740,11 @@ class endpoint : public config::transport_type, public config::endpoint_base { long m_open_handshake_timeout_dur; long m_close_handshake_timeout_dur; + long m_http_read_timeout_dur; long m_pong_timeout_dur; size_t m_max_message_size; size_t m_max_http_body_size; + size_t m_max_redirects; rng_type m_rng; diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp index 20f409dce..b94a11a74 100644 --- a/websocketpp/error.hpp +++ b/websocketpp/error.hpp @@ -146,7 +146,10 @@ enum value { extension_neg_failed, /// General transport error, consult more specific transport error code - transport_error + transport_error, + + /// Reading HTTP response timed out + http_read_response_timeout }; // enum value @@ -226,6 +229,8 @@ class category : public lib::error_category { return "Extension negotiation failed"; case error::transport_error: return "An error occurred in the underlying transport. Consult transport error code for more details."; + case error::http_read_response_timeout: + return "Reading the HTTP response timed out"; default: return "Unknown"; } diff --git a/websocketpp/frame.hpp b/websocketpp/frame.hpp index 34f2846b8..f2bd45bf6 100644 --- a/websocketpp/frame.hpp +++ b/websocketpp/frame.hpp @@ -613,7 +613,7 @@ inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) { if (offset == 0) { return prepared_key; } - if (lib::net::is_little_endian()) { + if (::std::endian::native == ::std::endian::little) { size_t temp = prepared_key << (sizeof(size_t)-offset)*8; return (prepared_key >> offset*8) | temp; } else { diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp index 02382f101..068e99c07 100644 --- a/websocketpp/http/constants.hpp +++ b/websocketpp/http/constants.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -55,8 +56,8 @@ typedef std::map attribute_list; */ typedef std::vector< std::pair > parameter_list; -/// Literal value of the HTTP header delimiter -static char const header_delimiter[] = "\r\n"; +/// The sequency used for new lines +static char const http_crlf[] = "\r\n"; /// Literal value of the HTTP header separator static char const header_separator[] = ":"; @@ -179,6 +180,18 @@ enum value { network_authentication_required = 511 }; +/// Given a status code value, return true if it is a redirect +/** + * + * @param[in] code The HTTP status code to check + * @return True if the status code is a redirect code + * @see websocketpp::http::status_code::value (list of valid codes) + */ +inline bool is_redirect(value code) { + // multiple_choices is not a valid redirect as we can't just go to the location specified in the Location header + return code > multiple_choices && code <= temporary_redirect; +} + /// Given a status code value, return the default status message /** * @@ -340,8 +353,14 @@ enum value { /// The transfer encoding is not supported unsupported_transfer_encoding, - /// The transfer encoding is unknown - unknown_transfer_encoding, + /// The content encoding is not supported + unsupported_content_encoding, + + /// The transfer encoding is invalid + unknown_transfer_encoding, + + /// The content encoding is invalid + unknown_content_encoding, /// A header line was missing a separator missing_header_separator, @@ -376,8 +395,12 @@ inline status_code::value get_status_code(error::value value) { return status_code::request_entity_too_large; case error::unsupported_transfer_encoding: return status_code::internal_server_error; - case error::unknown_transfer_encoding: - return status_code::bad_request; + case error::unsupported_content_encoding: + return status_code::internal_server_error; + case error::unknown_transfer_encoding: + return status_code::bad_request; + case error::unknown_content_encoding: + return status_code::bad_request; case error::missing_header_separator: return status_code::bad_request; case error::request_header_fields_too_large: @@ -446,6 +469,61 @@ inline lib::error_code make_error_code(error::value e) { } } // namespace error + +namespace content_encoding { +enum value : uint8_t { + compress = 1, + deflate, + gzip, + brotli, + zstd +}; + +inline std::string to_string(value encoding) { + switch (encoding) + { + case compress: return "compress"; + case deflate: return "deflate"; + case gzip: return "gzip"; + case brotli: return "br"; + case zstd: return "zstd"; + default: return {}; + } +} +inline std::optional from_string(const std::string& str) { + if (str == "compress") return compress; + else if (str == "deflate") return deflate; + else if (str == "gzip" || str == "x-gzip") return gzip; + else if (str == "br") return brotli; + else if (str == "zstd") return zstd; + else return {}; +} +} // namespace content_encoding + +namespace transfer_encoding { +enum value : uint8_t { + chunked, + compress, + deflate, + gzip +}; + +inline std::string to_string(value encoding) { + switch (encoding) + { + case chunked: + return "chunked"; + default: + return content_encoding::to_string(static_cast(encoding)); + } +} +} // namespace transfer_encoding + +const std::string Header_ContentLength = "Content-Length"; +const std::string Header_ContentEncoding = "Content-Encoding"; +const std::string Header_TransferEncoding = "Transfer-Encoding"; +const std::string Header_AcceptEncoding = "Accept-Encoding"; + } // namespace http } // namespace websocketpp diff --git a/websocketpp/http/encoding.hpp b/websocketpp/http/encoding.hpp new file mode 100644 index 000000000..96dbcbeeb --- /dev/null +++ b/websocketpp/http/encoding.hpp @@ -0,0 +1,397 @@ +// +// Created by Dan Toškan on 11.4.2024. +// + +#ifndef HTTP_ENCODING_HPP +#define HTTP_ENCODING_HPP + +#include + +#ifndef WEBSOCKETPP_WITH_GZIP +#define WEBSOCKETPP_WITH_GZIP 0 +#elif WEBSOCKETPP_WITH_GZIP +#include +#endif + +#ifndef WEBSOCKETPP_WITH_DEFLATE +#define WEBSOCKETPP_WITH_DEFLATE 0 +#elif WEBSOCKETPP_WITH_DEFLATE +#include +#endif + +#ifndef WEBSOCKETPP_WITH_BROTLI +#define WEBSOCKETPP_WITH_BROTLI 0 +#elif WEBSOCKETPP_WITH_BROTLI +#include +#include +#endif + +#ifndef WEBSOCKETPP_WITH_ZSTD +#define WEBSOCKETPP_WITH_ZSTD 0 +#elif WEBSOCKETPP_WITH_ZSTD +#include "zstd.h" +#endif + +namespace websocketpp +{ +namespace http +{ + +inline constexpr bool is_encoding_supported(content_encoding::value encoding) +{ + switch (encoding) + { + case content_encoding::gzip: return WEBSOCKETPP_WITH_GZIP; break; + case content_encoding::deflate: return WEBSOCKETPP_WITH_DEFLATE; break; + case content_encoding::brotli: return WEBSOCKETPP_WITH_BROTLI; break; + case content_encoding::compress: return false; break; // unsupported because browsers don't support it + case content_encoding::zstd: return WEBSOCKETPP_WITH_ZSTD; break; + default: break; + } + return false; +} + +} // namespace http + +namespace encoding +{ +#if WEBSOCKETPP_WITH_GZIP +namespace gzip +{ +constexpr int MOD_GZIP_ZLIB_WINDOWSIZE = 15; +constexpr int MOD_GZIP_ZLIB_CFACTOR = 9; +inline std::string compress(const std::string& str, lib::error_code& ec, + int compressionlevel = Z_BEST_COMPRESSION) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (deflateInit2(&zs, + compressionlevel, + Z_DEFLATED, + MOD_GZIP_ZLIB_WINDOWSIZE + 16, + MOD_GZIP_ZLIB_CFACTOR, + Z_DEFAULT_STRATEGY) != Z_OK + ) { + throw(std::runtime_error("deflateInit2 failed while compressing.")); + } + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); // set the z_stream's input + + int ret; + char outbuffer[32768]; + std::string outstring; + + // retrieve the compressed bytes blockwise + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = deflate(&zs, Z_FINISH); + + if (outstring.size() < zs.total_out) { + // append the block to the output string + outstring.append(outbuffer, + zs.total_out - outstring.size()); + } + } while (ret == Z_OK); + + deflateEnd(&zs); + + if (ret != Z_STREAM_END) { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib compression: (" << ret << ") " << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} + +inline std::string decompress(const std::string& str, lib::error_code& ec) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (inflateInit2(&zs, MOD_GZIP_ZLIB_WINDOWSIZE + 16) != Z_OK) + throw(std::runtime_error("inflateInit failed while decompressing.")); + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); + + int ret; + char outbuffer[32768]; + std::string outstring; + + // get the decompressed bytes blockwise using repeated calls to inflate + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = inflate(&zs, 0); + + if (outstring.size() < zs.total_out) { + outstring.append(outbuffer, + zs.total_out - outstring.size()); + } + + } while (ret == Z_OK); + + inflateEnd(&zs); + + if (ret != Z_STREAM_END) { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib decompression: (" << ret << ") " + << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} + +} // namespace gzip +#endif +#if WEBSOCKETPP_WITH_DEFLATE +namespace zlib +{ +constexpr int ZLIB_BUFFER_SIZE = 10240; +/** Compress a STL string using zlib with given compression level and return* the binary data. */ +inline std::string compress(const std::string& str, lib::error_code& ec, int compressionlevel = Z_BEST_COMPRESSION) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (deflateInit(&zs, compressionlevel) != Z_OK) + throw(std::runtime_error("deflateInit failed while compressing.")); + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); // set the z_stream's input + + int ret; + char outbuffer[ZLIB_BUFFER_SIZE]; + std::string outstring; + + // retrieve the compressed bytes blockwise + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = deflate(&zs, Z_FINISH); + + if (outstring.size() < zs.total_out) + { + // append the block to the output string + outstring.append(outbuffer, zs.total_out - outstring.size()); + } + } while (ret == Z_OK); + + deflateEnd(&zs); + + if (ret != Z_STREAM_END) + { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib compression: (" << ret << ") " << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} + +/** Decompress an STL string using zlib and return the original data. */ +inline std::string decompress(const std::string& str, lib::error_code& ec) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (inflateInit(&zs) != Z_OK) + throw(std::runtime_error("inflateInit failed while decompressing.")); + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); + + int ret; + char outbuffer[ZLIB_BUFFER_SIZE]; + std::string outstring; + + // get the decompressed bytes blockwise using repeated calls to inflate + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = inflate(&zs, 0); + + if (outstring.size() < zs.total_out) + { + outstring.append(outbuffer, zs.total_out - outstring.size()); + } + + } while (ret == Z_OK); + + inflateEnd(&zs); + + if (ret != Z_STREAM_END) + { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib decompression: (" << ret << ") " << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} +} // namespace zlib +#endif + +#if WEBSOCKETPP_WITH_BROTLI +namespace brotli +{ +constexpr size_t BROTLI_BUFFER_SIZE = 2048; +inline std::string compress(const std::string& data, lib::error_code& ec) +{ + auto instance = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + std::array buffer; + std::stringstream result; + + size_t available_in = data.length(), available_out = buffer.size(); + const uint8_t* next_in = reinterpret_cast(data.c_str()); + uint8_t* next_out = buffer.data(); + + do { + BrotliEncoderCompressStream(instance, BROTLI_OPERATION_FINISH, &available_in, &next_in, &available_out, &next_out, nullptr); + result.write(reinterpret_cast(buffer.data()), buffer.size() - available_out); + available_out = buffer.size(); + next_out = buffer.data(); + } while (!(available_in == 0 && BrotliEncoderIsFinished(instance))); + + BrotliEncoderDestroyInstance(instance); + return result.str(); +} + +inline std::string decompress(const std::string& data, lib::error_code& ec) +{ + auto instance = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + std::array buffer; + std::stringstream result; + + size_t available_in = data.length(), available_out = buffer.size(); + const uint8_t* next_in = reinterpret_cast(data.c_str()); + uint8_t* next_out = buffer.data(); + BrotliDecoderResult oneshot_result; + + do { + oneshot_result = BrotliDecoderDecompressStream(instance, &available_in, &next_in, &available_out, &next_out, nullptr); + result.write(reinterpret_cast(buffer.data()), buffer.size() - available_out); + available_out = buffer.size(); + next_out = buffer.data(); + } while (!(available_in == 0 && oneshot_result == BROTLI_DECODER_RESULT_SUCCESS)); + + BrotliDecoderDestroyInstance(instance); + return result.str(); +} +} // namespace brotli +#endif + +#if WEBSOCKETPP_WITH_ZSTD +namespace zstd { +inline std::string compress(const std::string& data, lib::error_code& ec, int compress_level = ZSTD_CLEVEL_DEFAULT) { + + size_t est_compress_size = ZSTD_compressBound(data.size()); + + std::string comp_buffer; + comp_buffer.resize(est_compress_size); + + auto compress_size = + ZSTD_compress((void*)comp_buffer.data(), est_compress_size, data.data(), + data.size(), compress_level); + + comp_buffer.resize(compress_size); + comp_buffer.shrink_to_fit(); + + return comp_buffer; +} + +inline std::string decompress(const std::string& data, lib::error_code& ec) { + + size_t const est_decomp_size = + ZSTD_getDecompressedSize(data.data(), data.size()); + + std::string decomp_buffer; + decomp_buffer.resize(est_decomp_size); + + size_t const decomp_size = ZSTD_decompress( + (void*)decomp_buffer.data(), est_decomp_size, data.data(), data.size()); + + decomp_buffer.resize(decomp_size); + decomp_buffer.shrink_to_fit(); + return decomp_buffer; +} + +} // namespace zstd +#endif + +inline std::string decompress(http::content_encoding::value encoding, bool is_transfer_encoding, const std::string& str, lib::error_code& ec) +{ + switch (encoding) + { +#if WEBSOCKETPP_WITH_DEFLATE + case http::content_encoding::deflate: + return zlib::decompress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_BROTLI + case http::content_encoding::brotli: + return brotli::decompress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_GZIP + case http::content_encoding::gzip: + return gzip::decompress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_ZSTD + case http::content_encoding::zstd: + return zstd::decompress(str, ec); + break; +#endif + default: break; + } + + ec = http::error::make_error_code(is_transfer_encoding ? http::error::unsupported_transfer_encoding : http::error::unsupported_content_encoding); + return {}; +} + +inline std::string compress(http::content_encoding::value encoding, bool is_transfer_encoding, const std::string& str, lib::error_code& ec) +{ + switch (encoding) + { +#if WEBSOCKETPP_WITH_DEFLATE + case http::content_encoding::deflate: + return zlib::compress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_BROTLI + case http::content_encoding::brotli: + return brotli::compress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_GZIP + case http::content_encoding::gzip: + return gzip::compress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_ZSTD + case http::content_encoding::zstd: + return zstd::compress(str, ec); + break; +#endif + default: break; + } + + ec = http::error::make_error_code(is_transfer_encoding ? http::error::unsupported_transfer_encoding : http::error::unsupported_content_encoding); + return {}; +} + +} // namespace encoding + +} // namespace websocketpp + +#endif // HTTP_CONSTANTS_HPP diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index c8c0eee78..df381143d 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -105,29 +105,34 @@ inline lib::error_code parser::remove_header(std::string const & key) return lib::error_code(); } -inline lib::error_code parser::set_body(std::string const & value) { +inline lib::error_code parser::set_body(std::string value) { lib::error_code ec; if (value.size() == 0) { - ec = remove_header("Content-Length"); + ec = remove_header(Header_ContentLength); if (ec) { return ec; } m_body.clear(); return lib::error_code(); } - if (value.size() > m_body_bytes_max) { + if (m_body_bytes_max && value.size() > m_body_bytes_max) { return error::make_error_code(error::body_too_large); } std::stringstream len; len << value.size(); - ec = replace_header("Content-Length", len.str()); + ec = replace_header(Header_ContentLength, len.str()); if (ec) { return ec; } - m_body = value; + m_body = std::move(value); return lib::error_code(); } +inline void parser::consume_body() +{ + m_body.clear(); +} + inline bool parser::parse_parameter_list(std::string const & in, parameter_list & out) const { @@ -141,30 +146,76 @@ inline bool parser::parse_parameter_list(std::string const & in, } inline bool parser::prepare_body(lib::error_code & ec) { - if (!get_header("Content-Length").empty()) { - std::string const & cl_header = get_header("Content-Length"); + ec.clear(); + + parameter_list ce_list; + if (!get_header_as_plist(Header_ContentEncoding, ce_list)) { + for (const auto& param : ce_list) { + auto encoding = content_encoding::from_string(param.first); + if (encoding) + { + m_content_encoding.push_back(*encoding); + } + else { + ec = error::make_error_code(error::unknown_content_encoding); + return false; + } + } + + if (m_content_encoding.size() > 3) + { + ec = error::make_error_code(error::unsupported_content_encoding); + return false; + } + } + + parameter_list te_list; + if (!get_header_as_plist(Header_TransferEncoding, te_list) && !te_list.empty()) { + for (const auto& param : te_list) { + if (param.first == "gzip" || param.first == "x-gzip") + m_transfer_encoding.push_back(transfer_encoding::gzip); + else if (param.first == "compress") + m_transfer_encoding.push_back(transfer_encoding::compress); + else if (param.first == "deflate") + m_transfer_encoding.push_back(transfer_encoding::deflate); + else if (param.first == "chunked") + m_transfer_encoding.push_back(transfer_encoding::chunked); + else { + ec = error::make_error_code(error::unknown_transfer_encoding); + return false; + } + } + + if (m_transfer_encoding.size() > 3) + { + ec = error::make_error_code(error::unsupported_transfer_encoding); + return false; + } + + if (std::find(m_transfer_encoding.begin(), m_transfer_encoding.end(), transfer_encoding::chunked) != m_transfer_encoding.end()) + return true; // no Content-Length for chunked encoding! + } + + const std::string cl_header = get_header(Header_ContentLength); + if (!cl_header.empty()) { char * end; - + // TODO: not 100% sure what the compatibility of this method is. Also, // I believe this will only work up to 32bit sizes. Is there a need for // > 4GiB HTTP payloads? - m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); - - if (m_body_bytes_needed > m_body_bytes_max) { + m_body_bytes_total = m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); + if (end != cl_header.cend().base()) { + ec = error::make_error_code(error::invalid_format); + return false; + } + + if (m_body_bytes_max && m_body_bytes_total > m_body_bytes_max) { ec = error::make_error_code(error::body_too_large); return false; } - - m_body_encoding = body_encoding::plain; - ec = lib::error_code(); - return true; - } else if (get_header("Transfer-Encoding") == "chunked") { - // ec = error::make_error_code(error::unsupported_transfer_encoding); - // TODO: support for chunked transfers? Is that too much HTTP logic? - //m_body_encoding = body_encoding::chunked; - return false; + + return m_body_bytes_needed; } else { - ec = lib::error_code(); return false; } } @@ -172,19 +223,53 @@ inline bool parser::prepare_body(lib::error_code & ec) { inline size_t parser::process_body(char const * buf, size_t len, lib::error_code & ec) { - if (m_body_encoding == body_encoding::plain) { - size_t processed = (std::min)(m_body_bytes_needed,len); - m_body.append(buf,processed); - m_body_bytes_needed -= processed; - ec = lib::error_code(); - return processed; - } else if (m_body_encoding == body_encoding::chunked) { - ec = error::make_error_code(error::unsupported_transfer_encoding); - return 0; - // TODO: support for chunked transfers? + if (!len) + return 0; + + if (std::find(m_transfer_encoding.begin(), m_transfer_encoding.end(), transfer_encoding::chunked) != m_transfer_encoding.end()) { + // for chunked encoding, read chunks of the body + if (m_body_bytes_needed) { // reading previously started chunk, same as plain encoding! + const size_t processed = std::min(m_body_bytes_needed, len); + m_body.append(buf, processed); + m_body_bytes_needed -= processed; + ec = lib::error_code(); + return processed; + } else { // new chunk + // sizes of chunks which are given by the first byte of the response body + const char* newline = std::search(buf, buf + len, http_crlf, http_crlf + sizeof(http_crlf) - 1); + if (newline == buf + len) + { + ec = error::make_error_code(error::invalid_format); + return 0; + } + + const std::string chunkSizeHex(buf, newline); + char * end; + m_body_bytes_needed = std::strtoul(chunkSizeHex.c_str(),&end,16); + m_body_bytes_total += m_body_bytes_needed; + if (end != chunkSizeHex.cend().base()) { + ec = error::make_error_code(error::invalid_format); + return 0; + } + + if (m_body_bytes_max && m_body_bytes_total > m_body_bytes_max) { + ec = error::make_error_code(error::body_too_large); + return 0; + } + + if (m_body_bytes_needed == 0) { // this is how the last chunk is marked + return len; // pretend we handled everything! + } + + const size_t processed = (newline - buf) + sizeof(http_crlf) - 1; + return processed + process_body(buf + processed, len - processed, ec); + } } else { - ec = error::make_error_code(error::unknown_transfer_encoding); - return 0; + const size_t processed = std::min(m_body_bytes_needed, len); + m_body.append(buf, processed); + m_body_bytes_needed -= processed; + ec = lib::error_code(); + return processed; } } diff --git a/websocketpp/http/impl/request.hpp b/websocketpp/http/impl/request.hpp index eae77a606..e46559f65 100644 --- a/websocketpp/http/impl/request.hpp +++ b/websocketpp/http/impl/request.hpp @@ -33,6 +33,7 @@ #include #include +#include namespace websocketpp { namespace http { @@ -41,13 +42,13 @@ namespace parser { inline size_t request::consume(char const * buf, size_t len, lib::error_code & ec) { size_t bytes_processed = 0; - + if (m_ready) { // the request is already complete. End immediately without reading. ec = lib::error_code(); return 0; } - + if (m_body_bytes_needed > 0) { // The headers are complete, but we are still expecting more body // bytes. Process body bytes. @@ -66,7 +67,7 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // at this point we have an incomplete request still waiting for headers // copy new candidate bytes into our local buffer. This buffer may have - // leftover bytes from previous calls. Not all of these bytes are + // leftover bytes from previous calls. Not all of these bytes are // necessarily header bytes (they might be body or even data after this // request entirely for a keepalive request) m_buf->append(buf,len); @@ -80,8 +81,8 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e end = std::search( begin, m_buf->end(), - header_delimiter, - header_delimiter+sizeof(header_delimiter)-1 + http_crlf, + http_crlf+sizeof(http_crlf)-1 ); if (end == m_buf->end()) { @@ -95,9 +96,9 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e } // We are out of bytes but not over any limits yet. Discard the - // processed bytes and copy the remaining unprecessed bytes to the + // processed bytes and copy the remaining unprecessed bytes to the // beginning of the buffer in prep for another call to consume. - + // If there are no processed bytes in the buffer right now don't // copy the unprocessed ones over themselves. if (begin != m_buf->begin()) { @@ -113,8 +114,8 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // represents a line to be processed // update count of header bytes read so far - m_header_bytes += (end-begin+sizeof(header_delimiter)); - + m_header_bytes += (end-begin+sizeof(http_crlf)); + if (m_header_bytes > max_header_size) { // This read exceeded max header size @@ -138,21 +139,35 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // other logic. bytes_processed = ( len - static_cast(m_buf->end()-end) - + sizeof(header_delimiter) - 1 + + sizeof(http_crlf) - 1 ); // frees memory used temporarily during request parsing m_buf.reset(); // if this was not an upgrade request and has a content length - // continue capturing content-length bytes and expose them as a + // continue capturing content-length bytes and expose them as a // request body. - + bool need_more = prepare_body(ec); if (ec) { return 0; } + parameter_list acc_enc_list; + if (!get_header_as_plist(Header_AcceptEncoding, acc_enc_list)) { + m_accept_encoding = std::vector(); + for (const auto& param : acc_enc_list) { + auto encoding = content_encoding::from_string(param.first); + if (encoding) { + m_accept_encoding->push_back(*encoding); + } else { + ec = error::make_error_code(error::unknown_content_encoding); + return 0; + } + } + } + if (need_more) { bytes_processed += process_body(buf+bytes_processed,len-bytes_processed,ec); if (ec) { @@ -161,15 +176,13 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e if (body_ready()) { m_ready = true; } - ec = lib::error_code(); - return bytes_processed; } else { m_ready = true; - - // return number of bytes processed (starting bytes - bytes left) - ec = lib::error_code(); - return bytes_processed; } + + // return number of bytes processed (starting bytes - bytes left) + ec = lib::error_code(); + return bytes_processed; } else { // we got a line with content if (m_method.empty()) { @@ -187,7 +200,7 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // if we got here it means there is another header line to read. // advance our cursor to the first character after the most recent // delimiter found. - begin = end+(sizeof(header_delimiter)-1); + begin = end+(sizeof(http_crlf)-1); } } @@ -257,6 +270,24 @@ inline lib::error_code request::process(std::string::iterator begin, std::string return set_version(std::string(cursor_end+1,end)); } +inline void request::set_accepted_encodings(std::vector encodings) +{ + m_accept_encoding = std::move(encodings); + remove_header(Header_AcceptEncoding); + for (auto it = m_accept_encoding->begin(); it != m_accept_encoding->end();) + { + if (!is_encoding_supported(*it)) + { + it = m_accept_encoding->erase(it); + continue; + } + + append_header(Header_AcceptEncoding, content_encoding::to_string(*it)); + + ++it; + } +} + } // namespace parser } // namespace http } // namespace websocketpp diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp index 8890ee103..e353493b7 100644 --- a/websocketpp/http/impl/response.hpp +++ b/websocketpp/http/impl/response.hpp @@ -34,19 +34,28 @@ #include #include +#include namespace websocketpp { namespace http { namespace parser { +inline void response::on_parsing_completed(lib::error_code & ec) { + m_state = state::DONE; + + for (auto decode = m_content_encoding.rbegin(); !ec && decode != m_content_encoding.rend(); decode++) { + m_body = encoding::decompress(*decode, false, m_body, ec); + } +} + inline size_t response::consume(char const * buf, size_t len, lib::error_code & ec) { - if (m_state == DONE) { + if (m_state == state::DONE) { // the response is already complete. End immediately without reading. - ec = lib::error_code(); + ec.clear(); return 0; } - if (m_state == BODY) { + if (m_state == state::BODY) { // The headers are complete, but we are still expecting more body // bytes. Process body bytes. return this->process_body(buf,len,ec); @@ -70,8 +79,8 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & end = std::search( begin, m_buf->end(), - header_delimiter, - header_delimiter + sizeof(header_delimiter) - 1 + http_crlf, + http_crlf + sizeof(http_crlf) - 1 ); if (end == m_buf->end()) { @@ -95,9 +104,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & m_buf->resize(static_cast(end-begin)); } - m_read += len; - - ec = lib::error_code(); + ec.clear(); return len; } @@ -105,7 +112,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // represents a line to be processed // update count of header bytes read so far - m_header_bytes += (end-begin+sizeof(header_delimiter)); + m_header_bytes += (end-begin+sizeof(http_crlf)); if (m_header_bytes > max_header_size) { // This read exceeded max header size @@ -119,34 +126,24 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // If we are still looking for a response line then this request // is incomplete - if (m_state == RESPONSE_LINE) { + if (m_state == state::RESPONSE_LINE) { ec = error::make_error_code(error::incomplete_request); return 0; } - // TODO: grab content-length - std::string length = get_header("Content-Length"); - - if (length.empty()) { - // no content length found, read indefinitely? - m_read = 0; - } else { - std::istringstream ss(length); - - if ((ss >> m_read).fail()) { - ec = error::make_error_code(error::invalid_format); - return 0; - } - } + // transition state to reading the response body + m_state = state::BODY; - // transition state to reading the response body - m_state = BODY; + const bool need_more = prepare_body(ec); + if (ec) { + return 0; + } // calculate how many bytes in the local buffer are bytes we didn't // use for the headers. size_t read = ( len - static_cast(m_buf->end() - end) - + sizeof(header_delimiter) - 1 + + sizeof(http_crlf) - 1 ); // if there were bytes left process them as body bytes. @@ -154,23 +151,30 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // It is possible that there are still some bytes not read. These // will be 'returned' to the caller by having the return value be // less than len. - if (read < len) { + if (read < len && need_more) { read += this->process_body(buf+read,(len-read),ec); } if (ec) { return 0; } + if (m_body_bytes_needed == 0) { + on_parsing_completed(ec); + if (ec) { + return 0; + } + } + // frees memory used temporarily during header parsing m_buf.reset(); - ec = lib::error_code(); + ec.clear(); return read; } else { // we got a line - if (m_state == RESPONSE_LINE) { + if (m_state == state::RESPONSE_LINE) { ec = this->process(begin,end); - m_state = HEADERS; + m_state = state::HEADERS; } else { ec = this->process_header(begin,end); } @@ -182,7 +186,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // if we got here it means there is another header line to read. // advance our cursor to the first character after the most recent // delimiter found. - begin = end+(sizeof(header_delimiter) - 1); + begin = end+(sizeof(http_crlf) - 1); } } @@ -320,30 +324,19 @@ inline lib::error_code response::process(std::string::iterator begin, } inline size_t response::process_body(char const * buf, size_t len, lib::error_code & ec) { - // If no content length was set then we read forever and never set m_ready - if (m_read == 0) { - m_state = DONE; - ec = lib::error_code(); - return 0; - } - - // Otherwise m_read is the number of bytes left. - size_t to_read; - - if (len >= m_read) { - // if we have more bytes than we need read, read only the amount needed - // then set done state - to_read = m_read; - m_state = DONE; - } else { - // we need more bytes than are available, read them all - to_read = len; - } - - m_body.append(buf,to_read); - m_read -= to_read; - ec = lib::error_code(); - return to_read; + size_t processed = 0; + do + { + const size_t processed_chunk = parser::process_body(buf, len, ec); + buf += processed_chunk; + len -= processed_chunk; + processed += processed_chunk; + } while (!ec && m_body_bytes_needed && len); + + if (ec || m_body_bytes_needed == 0) + on_parsing_completed(ec); + + return processed; } } // namespace parser diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index 2b4d194c9..ffbc23db5 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -49,14 +49,6 @@ namespace state { }; } -namespace body_encoding { - enum value { - unknown, - plain, - chunked - }; -} - typedef std::map header_list; /// Read and return the next token in the stream @@ -400,9 +392,12 @@ class parser { parser() : m_header_bytes(0) , m_body_bytes_needed(0) + , m_body_bytes_total(0) , m_body_bytes_max(max_body_size) - , m_body_encoding(body_encoding::unknown) {} - + {} + + virtual ~parser() {} + /// Get the HTTP version string /** * @return The version string for this parser @@ -437,7 +432,7 @@ class parser { * * @param [in] key The name/key of the HTTP header to use as input. * @param [out] out The parameter list to store extracted parameters in. - * @return Whether or not the input was a valid parameter list. + * @return Whether or not the input was a valid parameter list. (true means invalid, false means valid) */ bool get_header_as_plist(std::string const & key, parameter_list & out) const; @@ -459,13 +454,13 @@ class parser { * will be appended to the existing value. * * Note: per HTTP specs header values are compared case insensitively. - * + * * @todo Should there be any restrictions on which keys are allowed? * * @see replace_header * * @since 0.9.0 (return value added, exceptions removed) - * + * * @param [in] key The name/key of the header to append to. * @param [in] val The value to append. * @return A status code describing the outcome of the operation. @@ -483,7 +478,7 @@ class parser { * @see append_header * * @since 0.9.0 (return value added) - * + * * @param [in] key The name/key of the header to append to. * @param [in] val The value to append. * @return A status code describing the outcome of the operation. @@ -498,7 +493,7 @@ class parser { * Note: per HTTP specs header values are compared case insensitively. * * @since 0.9.0 (return value added) - * + * * @param [in] key The name/key of the header to remove. * @return A status code describing the outcome of the operation. */ @@ -516,6 +511,27 @@ class parser { return m_body; } + /// Get the progress of the HTTP body download relative to the expected body size. + /** + * Effectively calculated as (downloaded bytes) / get_total_body_size() + * + * @return The progress (0 to 1 range) + */ + size_t get_transferred_body_size() const { + return m_body_bytes_total ? (m_body_bytes_total - m_body_bytes_needed) : m_body.size(); + } + + /// Get the expected HTTP body size + /** + * For plain encoding, this is the Content-Length + * For chunked encoding, this is the total number of bytes transfered (changes as new chunks are processed). + * + * @return The total size of the HTTP body. + */ + size_t get_total_body_size() const { + return m_body_bytes_total; + } + /// Set body content /** * Set the body content of the HTTP response to the parameter string. Note @@ -524,11 +540,19 @@ class parser { * via replace_header("Content-Length") after calling set_body() * * @since 0.9.0 (return value added) - * + * * @param value String data to include as the body content. * @return A status code describing the outcome of the operation. */ - lib::error_code set_body(std::string const & value); + lib::error_code set_body(std::string value); + + /// Consume body content (keep string at current capacity ) + /** + * Keeps the body string at current capacity, but resets its size + * + * @since 0.8.5 + */ + void consume_body(); /// Get body size limit /** @@ -543,6 +567,30 @@ class parser { return m_body_bytes_max; } + /// Get transfer encoding + /** + * The list of transfer encodings of the body, the order in which they were applied. + * + * @since 0.9.0 + * + * @return The list of transfer encodings of the body, the order in which they were applied. + */ + const std::vector& get_transfer_encoding() const { + return m_transfer_encoding; + } + + /// Get content encoding + /** + * The list of content encodings of the body, the order in which they were applied. + * + * @since 0.9.0 + * + * @return The list of content encodings of the body, the order in which they were applied. + */ + const std::vector& get_content_encoding() const { + return m_content_encoding; + } + /// Set body size limit /** * Set the maximum number of bytes to parse and buffer before canceling a @@ -560,7 +608,7 @@ class parser { /** * @param [in] in The input string. * @param [out] out The parameter list to store extracted parameters in. - * @return Whether or not the input was a valid parameter list. + * @return Whether or not the input was a valid parameter list (true means invalid, false means valid) */ bool parse_parameter_list(std::string const & in, parameter_list & out) const; @@ -568,7 +616,7 @@ class parser { /// Process a header line /** * @since 0.9.0 (return value added, exceptions removed) - * + * * @param [in] begin An iterator to the beginning of the sequence. * @param [in] end An iterator to the end of the sequence. * @return A status code describing the outcome of the operation. @@ -605,7 +653,7 @@ class parser { * @param [out] ec A status code describing the outcome of the operation. * @return The number of bytes processed */ - size_t process_body(char const * buf, size_t len, lib::error_code & ec); + virtual size_t process_body(char const * buf, size_t len, lib::error_code & ec); /// Check if the parser is done parsing the body /** @@ -630,13 +678,15 @@ class parser { std::string m_version; header_list m_headers; - - size_t m_header_bytes; - - std::string m_body; - size_t m_body_bytes_needed; - size_t m_body_bytes_max; - body_encoding::value m_body_encoding; + + size_t m_header_bytes; + + std::string m_body; + size_t m_body_bytes_needed; + size_t m_body_bytes_total; + size_t m_body_bytes_max; + std::vector m_transfer_encoding; + std::vector m_content_encoding; }; } // namespace parser diff --git a/websocketpp/http/request.hpp b/websocketpp/http/request.hpp index ca8c5ac8c..aebd8bebb 100644 --- a/websocketpp/http/request.hpp +++ b/websocketpp/http/request.hpp @@ -124,6 +124,16 @@ class request : public parser { return m_uri; } + const std::optional>& get_accepted_encodings() const { + return m_accept_encoding; + } + + bool accepts_encoding(content_encoding::value encoding) const { + return m_accept_encoding && std::find(m_accept_encoding->begin(), m_accept_encoding->end(), encoding) != m_accept_encoding->end(); + } + + void set_accepted_encodings(std::vector encodings); + private: /// Helper function for message::consume. Process request line /** @@ -135,10 +145,11 @@ class request : public parser { */ lib::error_code process(std::string::iterator begin, std::string::iterator end); - lib::shared_ptr m_buf; - std::string m_method; - std::string m_uri; - bool m_ready; + lib::shared_ptr m_buf; + std::string m_method; + std::string m_uri; + bool m_ready; + std::optional> m_accept_encoding; }; } // namespace parser diff --git a/websocketpp/http/response.hpp b/websocketpp/http/response.hpp index 64bce76a8..c446ba773 100644 --- a/websocketpp/http/response.hpp +++ b/websocketpp/http/response.hpp @@ -59,11 +59,17 @@ class response : public parser { typedef response type; typedef lib::shared_ptr ptr; + enum class state { + RESPONSE_LINE = 0, + HEADERS = 1, + BODY = 2, + DONE = 3 + }; + response() - : m_read(0) - , m_buf(lib::make_shared()) + : m_buf(lib::make_shared()) , m_status_code(status_code::uninitialized) - , m_state(RESPONSE_LINE) {} + , m_state(state::RESPONSE_LINE) {} /// Process bytes in the input buffer /** @@ -142,12 +148,12 @@ class response : public parser { * @note will never return true if the content length header is not present */ bool ready() const { - return m_state == DONE; + return m_state == state::DONE; } - /// Returns true if the response headers are fully parsed. - bool headers_ready() const { - return (m_state == BODY || m_state == DONE); + /// Returns true if a particular state has been passed + bool has_received(state s) const { + return m_state > s; } /// Returns the full raw response @@ -194,18 +200,11 @@ class response : public parser { /// Helper function for consume. Process response line lib::error_code process(std::string::iterator begin, std::string::iterator end); - /// Helper function for processing body bytes - size_t process_body(char const * buf, size_t len, lib::error_code & ec); + virtual size_t process_body(char const * buf, size_t len, lib::error_code & ec) override; - enum state { - RESPONSE_LINE = 0, - HEADERS = 1, - BODY = 2, - DONE = 3 - }; + void on_parsing_completed(lib::error_code & ec); std::string m_status_msg; - size_t m_read; lib::shared_ptr m_buf; status_code::value m_status_code; state m_state; diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 6ab7414af..db91e0472 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -38,6 +38,8 @@ #include #include +#include + #include #include #include @@ -109,7 +111,7 @@ lib::error_code connection::send(typename config::message_type::ptr msg) { scoped_lock_type lock(m_connection_state_lock); - if (m_state != session::state::open) { + if (m_state != session::state::open || m_is_http) { return error::make_error_code(error::invalid_state); } } @@ -159,7 +161,7 @@ void connection::ping(std::string const& payload, lib::error_code& ec) { { scoped_lock_type lock(m_connection_state_lock); - if (m_state != session::state::open) { + if (m_state != session::state::open || m_is_http) { std::stringstream ss; ss << "connection::ping called from invalid state " << m_state; m_alog->write(log::alevel::devel,ss.str()); @@ -442,6 +444,7 @@ template void connection::set_uri(uri_ptr uri) { //scoped_lock_type lock(m_connection_state_lock); m_uri = uri; + m_is_http = m_uri && m_uri->get_type() == uri::http; } @@ -600,7 +603,57 @@ void connection::set_status(http::status_code::value code, #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::set_body(std::string const & value, +void connection::check_body_encoding(std::string & body, const http::body_options& opts, lib::error_code & ec) +{ + remove_header(http::Header_ContentEncoding, ec); + if (opts.input_encoding) + { + if (m_request.accepts_encoding(*opts.input_encoding)) { + append_header(http::Header_ContentEncoding, http::content_encoding::to_string(*opts.input_encoding), ec); + return; // body already in correct format! + } + else { + body = encoding::decompress(*opts.input_encoding, false, body, ec); + } + } + + for (http::content_encoding::value encode_body : opts.output_encoding) { + if (ec) break; + + if (!http::is_encoding_supported(encode_body)) continue; + + if (!m_request.accepts_encoding(encode_body)) continue; + + body = encoding::compress(encode_body, true, body, ec); + if (!ec) { + append_header(http::Header_ContentEncoding, http::content_encoding::to_string(encode_body), ec); + } + + break; // only encode in a single format + } + + for (http::transfer_encoding::value encode_body : opts.transfer_encoding) { + if (ec) break; + + if (encode_body == http::transfer_encoding::chunked) continue; + + const http::content_encoding::value as_content_encoding = static_cast(encode_body); + + if (!http::is_encoding_supported(as_content_encoding)) continue; + + if (!m_request.accepts_encoding(as_content_encoding)) continue; + + body = encoding::compress(as_content_encoding, true, body, ec); + if (!ec) { + append_header(http::Header_ContentEncoding, http::content_encoding::to_string(as_content_encoding), ec); + } + + // allow multiple encodings + } +} + +template +void connection::set_body(std::string const & value, const http::body_options& opts, lib::error_code & ec) { if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { @@ -608,14 +661,20 @@ void connection::set_body(std::string const & value, return; } - ec = m_response.set_body(value); + std::string body(value); + check_body_encoding(body, opts, ec); + if (ec) { + return; + } + + ec = m_response.set_body(std::move(body)); } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::set_body(std::string const & value) { +void connection::set_body(std::string const & value, const http::body_options& opts) { lib::error_code ec; - this->set_body(value, ec); + this->set_body(value, opts, ec); if (ec) { throw exception("Call to set_body from invalid state", ec); } @@ -624,7 +683,7 @@ void connection::set_body(std::string const & value) { #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ template -void connection::set_body(std::string && value, +void connection::set_body(std::string && value, const http::body_options& opts, lib::error_code & ec) { if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { @@ -632,14 +691,19 @@ void connection::set_body(std::string && value, return; } + check_body_encoding(value, opts, ec); + if (ec) { + return; + } + ec = m_response.set_body(std::move(value)); } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::set_body(std::string && value) { +void connection::set_body(std::string && value, const http::body_options& opts) { lib::error_code ec; - this->set_body(std::move(value), ec); + this->set_body(std::move(value), opts, ec); if (ec) { throw exception("Call to set_body from invalid state", ec); } @@ -647,6 +711,20 @@ void connection::set_body(std::string && value) { #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ +template +void connection::set_request(request_type req, + lib::error_code& ec) +{ + if (m_internal_state != istate::USER_INIT) { + ec = error::make_error_code(error::invalid_state); + return; + } + + const size_t max_body_size = req.get_max_body_size(); + m_request = std::move(req); + m_request.set_max_body_size(max_body_size); +} + template void connection::append_header(std::string const & key, std::string const & val, lib::error_code & ec) @@ -866,11 +944,54 @@ void connection::handle_transport_init(lib::error_code const & ec) { m_internal_state = istate::READ_HTTP_REQUEST; this->read_handshake(1); } else { - // We are a client. Set the processor to the version specified in the - // config file and send a handshake request. - m_internal_state = istate::WRITE_HTTP_REQUEST; - m_processor = get_processor(config::client_version); - this->send_http_request(); + // We are a client. + m_internal_state = istate::WRITE_HTTP_REQUEST; + if (!m_is_http) + { + // Set the processor to the version specified in the config file and send a handshake request. + m_processor = get_processor(config::client_version); + + // Have the protocol processor fill in the appropriate fields based on the + // selected client version + if (m_processor) { + lib::error_code ec; + ec = m_processor->client_handshake_request(m_request,m_uri, + m_requested_subprotocols); + + if (ec) { + log_err(log::elevel::fatal,"Internal library error: Processor",ec); + return; + } + } else { + m_elog->write(log::elevel::fatal,"Internal library error: missing processor"); + return; + } + } else { // regular http request + if (m_request.get_version().empty()) + m_request.set_version("HTTP/1.1"); + + if (m_request.get_method().empty()) + m_request.set_method("GET"); + + std::string host_port; + std::string resource; + + const uri request_uri(m_request.get_uri()); + if (request_uri.is_absolute()) { + host_port = request_uri.get_host_port(); + resource = request_uri.get_resource(); + } else { + host_port = m_uri->get_host_port(); + resource = m_uri->get_resource(); + if (resource.ends_with('/')) // prevent double slashes! + resource.pop_back(); + resource += request_uri.get_resource(); // get_resource should always have a leading slash + } + + m_request.replace_header("Host", host_port);; + m_request.set_uri(resource); + } + this->send_http_request(); } } @@ -1022,12 +1143,13 @@ void connection::handle_read_handshake(lib::error_code const & ec, m_internal_state = istate::PROCESS_HTTP_REQUEST; // We have the complete request. Process it. - lib::error_code handshake_ec = this->process_handshake_request(); + lib::error_code handshake_ec; + session::validation::value action = this->process_handshake_request(handshake_ec); // Write a response if this is a websocket connection or if it is an // HTTP connection for which the response has not been deferred or // started yet by a different system (i.e. still in init state). - if (!m_is_http || m_http_state == session::http_state::init) { + if ((!m_is_http && action != session::validation::defer) || (m_is_http && m_http_state == session::http_state::init)) { this->write_http_response(handshake_ec); } } else { @@ -1153,11 +1275,11 @@ void connection::handle_read_frame(lib::error_code const & ec, lib::error_code consume_ec; - if (m_alog->static_test(log::alevel::devel)) { + /*if (m_alog->static_test(log::alevel::devel)) { std::stringstream s; s << "Processing Bytes: " << utility::to_hex(reinterpret_cast(m_buf)+p,bytes_transferred-p); m_alog->write(log::alevel::devel,s.str()); - } + }*/ p += m_processor->consume( reinterpret_cast(m_buf)+p, @@ -1285,7 +1407,7 @@ lib::error_code connection::initialize_processor() { } template -lib::error_code connection::process_handshake_request() { +session::validation::value connection::process_handshake_request(lib::error_code & ec) { m_alog->write(log::alevel::devel,"process handshake request"); if (!processor::is_websocket_handshake(m_request)) { @@ -1293,15 +1415,13 @@ lib::error_code connection::process_handshake_request() { m_alog->write(log::alevel::devel,"HTTP REQUEST"); // extract URI from request - m_uri = processor::get_uri_from_host( - m_request, - (transport_con_type::is_secure() ? "https" : "http") - ); + m_uri = processor::get_uri_from_host(m_request, uri::http, transport_con_type::is_secure()); if (!m_uri->get_valid()) { m_alog->write(log::alevel::devel, "Bad request: failed to parse uri"); m_response.set_status(http::status_code::bad_request); - return error::make_error_code(error::invalid_uri); + ec = error::make_error_code(error::invalid_uri); + return session::validation::reject; } if (m_http_handler) { @@ -1309,24 +1429,27 @@ lib::error_code connection::process_handshake_request() { m_http_handler(m_connection_hdl); if (m_state == session::state::closed) { - return error::make_error_code(error::http_connection_ended); + ec = error::make_error_code(error::http_connection_ended); + return session::validation::reject; } } else { m_response.set_status(http::status_code::upgrade_required); - return error::make_error_code(error::upgrade_required); + ec = error::make_error_code(error::upgrade_required); + return session::validation::reject; } - return lib::error_code(); + ec.clear(); + return session::validation::reject; } - lib::error_code ec = m_processor->validate_handshake(m_request); + ec = m_processor->validate_handshake(m_request); // Validate: make sure all required elements are present. if (ec){ // Not a valid handshake request m_alog->write(log::alevel::devel, "Bad request " + ec.message()); m_response.set_status(http::status_code::bad_request); - return ec; + return session::validation::reject; } // Read extension parameters and set up values necessary for the end user @@ -1339,7 +1462,8 @@ lib::error_code connection::process_handshake_request() { // a failed connection attempt. m_elog->write(log::elevel::info, "Bad request: " + neg_results.first.message()); m_response.set_status(http::status_code::bad_request); - return neg_results.first; + ec = neg_results.first; + return session::validation::reject; } else if (neg_results.first) { // There was a fatal error in extension processing that is probably our // fault. Consider extension negotiation to have failed and continue as @@ -1363,7 +1487,8 @@ lib::error_code connection::process_handshake_request() { if (!m_uri->get_valid()) { m_alog->write(log::alevel::devel, "Bad request: failed to parse uri"); m_response.set_status(http::status_code::bad_request); - return error::make_error_code(error::invalid_uri); + ec = error::make_error_code(error::invalid_uri); + return session::validation::reject; } // extract subprotocols @@ -1375,7 +1500,20 @@ lib::error_code connection::process_handshake_request() { } // Ask application to validate the connection - if (!m_validate_handler || m_validate_handler(m_connection_hdl)) { + session::validation::value action = session::validation::accept; + if (m_validate_handler) + action = m_validate_handler(m_connection_hdl); + + if (action != session::validation::defer) + ec = finalize_handshake_response(action == session::validation::accept); + + return action; +} + +template +lib::error_code connection::finalize_handshake_response(bool accept) { + lib::error_code ec; + if (accept) { m_response.set_status(http::status_code::switching_protocols); // Write the appropriate response headers based on request and @@ -1383,16 +1521,13 @@ lib::error_code connection::process_handshake_request() { ec = m_processor->process_handshake(m_request,m_subprotocol,m_response); if (ec) { - std::stringstream s; - s << "Processing error: " << ec << "(" << ec.message() << ")"; - m_alog->write(log::alevel::devel, s.str()); + log_err(log::elevel::devel, "Processing", ec); m_response.set_status(http::status_code::internal_server_error); - return ec; } } else { // User application has rejected the handshake - m_alog->write(log::alevel::devel, "USER REJECT"); + m_alog->write(log::alevel::devel, "WebSocket connection rejected"); // Use Bad Request if the user handler did not provide a more // specific http response error code. @@ -1401,10 +1536,18 @@ lib::error_code connection::process_handshake_request() { m_response.set_status(http::status_code::bad_request); } - return error::make_error_code(error::rejected); + ec = error::make_error_code(error::rejected); } - return lib::error_code(); + return ec; +} + +template +lib::error_code connection::deferred_accept(bool accept) +{ + const lib::error_code ec = finalize_handshake_response(accept); + this->write_http_response(ec); + return get_ec(); } template @@ -1437,14 +1580,14 @@ void connection::write_http_response(lib::error_code const & ec) { // have the processor generate the raw bytes for the wire (if it exists) if (m_processor) { - m_handshake_buffer = m_processor->get_raw(m_response); + m_http_message_buffer = m_processor->get_raw(m_response); } else { // a processor wont exist for raw HTTP responses. - m_handshake_buffer = m_response.raw(); + m_http_message_buffer = m_response.raw(); } if (m_alog->static_test(log::alevel::devel)) { - m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer); + m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_http_message_buffer); if (!m_response.get_header("Sec-WebSocket-Key3").empty()) { m_alog->write(log::alevel::devel, utility::to_hex(m_response.get_header("Sec-WebSocket-Key3"))); @@ -1453,8 +1596,8 @@ void connection::write_http_response(lib::error_code const & ec) { // write raw bytes transport_con_type::async_write( - m_handshake_buffer.data(), - m_handshake_buffer.size(), + m_http_message_buffer.data(), + m_http_message_buffer.size(), lib::bind( &type::handle_write_http_response, type::get_shared(), @@ -1552,23 +1695,7 @@ void connection::send_http_request() { // TODO: origin header? - // Have the protocol processor fill in the appropriate fields based on the - // selected client version - if (m_processor) { - lib::error_code ec; - ec = m_processor->client_handshake_request(m_request,m_uri, - m_requested_subprotocols); - - if (ec) { - log_err(log::elevel::fatal,"Internal library error: Processor",ec); - return; - } - } else { - m_elog->write(log::elevel::fatal,"Internal library error: missing processor"); - return; - } - - // Unless the user has overridden the user agent, send generic WS++ UA. + // Unless the user has overridden the user agent, send generic UA. if (m_request.get_header("User-Agent").empty()) { if (!m_user_agent.empty()) { m_request.replace_header("User-Agent",m_user_agent); @@ -1577,10 +1704,10 @@ void connection::send_http_request() { } } - m_handshake_buffer = m_request.raw(); + m_http_message_buffer = m_request.raw(); if (m_alog->static_test(log::alevel::devel)) { - m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer); + m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_http_message_buffer); } if (m_open_handshake_timeout_dur > 0) { @@ -1595,8 +1722,8 @@ void connection::send_http_request() { } transport_con_type::async_write( - m_handshake_buffer.data(), - m_handshake_buffer.size(), + m_http_message_buffer.data(), + m_http_message_buffer.size(), lib::bind( &type::handle_send_http_request, type::get_shared(), @@ -1619,6 +1746,8 @@ void connection::handle_send_http_request(lib::error_code const & ec) { ecm = error::make_error_code(error::invalid_state); } else { m_internal_state = istate::READ_HTTP_RESPONSE; + if (m_is_http) + m_state = session::state::open; } } else if (m_state == session::state::closed) { // The connection was canceled while the response was being sent, @@ -1645,6 +1774,27 @@ void connection::handle_send_http_request(lib::error_code const & ec) { return; } + if (m_is_http) + { + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + // Start a timer so we don't wait forever for the http body + // it's okay to use the handskahe timer as it's not being used by anything else + if (m_http_response_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_http_response_timeout_dur, + lib::bind( + &type::handle_read_response_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + } + transport_con_type::async_read_at_least( 1, m_buf, @@ -1669,7 +1819,7 @@ void connection::handle_read_http_response(lib::error_code const & ec, if (!ecm) { scoped_lock_type lock(m_connection_state_lock); - if (m_state == session::state::connecting) { + if (m_state == session::state::connecting || (m_is_http && m_state == session::state::open)) { if (m_internal_state != istate::READ_HTTP_RESPONSE) { ecm = error::make_error_code(error::invalid_state); } @@ -1685,22 +1835,29 @@ void connection::handle_read_http_response(lib::error_code const & ec, } } - if (ecm) { - if (ecm == transport::error::eof && m_state == session::state::closed) { + bool should_parse = !ecm; + if (ecm.value() == transport::error::eof) { + if (m_state == session::state::closed) { // we expect to get eof if the connection is closed already m_alog->write(log::alevel::devel, "got (expected) eof/state error from closed con"); return; - } - - log_err(log::elevel::rerror,"handle_read_http_response",ecm); - this->terminate(ecm); - return; - } + } else if (m_is_http) { + // for http requests, it's normal to receive EOF + should_parse = true; + } + } + + if (!should_parse) + { + log_err(log::elevel::rerror,"handle_read_http_response",ecm); + this->terminate(ecm); + return; + } size_t bytes_processed = 0; - lib::error_code consume_ec; + lib::error_code consume_ec(ecm); bytes_processed = m_response.consume(m_buf, bytes_transferred, consume_ec); if (consume_ec) { @@ -1712,13 +1869,74 @@ void connection::handle_read_http_response(lib::error_code const & ec, return; } - m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); - - if (m_response.headers_ready()) { - if (m_handshake_timer) { - m_handshake_timer->cancel(); - m_handshake_timer.reset(); - } + // m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); + + if (m_response.has_received(response_type::state::HEADERS)) { + if (!m_is_http) + { + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + } + // follow redirect if possible + if (m_max_redirects && http::status_code::is_redirect(m_response.get_status_code())) { + m_max_redirects--; + + const uri redirect_uri(m_response.get_header("Location")); + if (redirect_uri.get_valid()) { + std::string host_port; + std::string resource; + + if (redirect_uri.is_absolute()) { + host_port = redirect_uri.get_host_port(); + resource = redirect_uri.get_resource(); + } else { + host_port = m_uri->get_host_port(); + resource = m_uri->get_resource(); + if (resource.ends_with('/')) // prevent double slashes! + resource.pop_back(); + resource += redirect_uri.get_resource(); // get_resource should always have a leading slash + } + + m_request.replace_header("Host", host_port);; + m_request.set_uri(resource); + this->send_http_request(); + return; + } + } + + if (m_is_http) { + if (m_progress_handler) + m_progress_handler(m_connection_hdl, m_response.get_transferred_body_size(), m_response.get_total_body_size()); + + if (m_response.has_received(response_type::state::BODY)) + { + this->terminate({}); + } else { + // The HTTP parser reported that it was not ready and wants more data. + // Assert that it actually consumed all the data present before overwriting + // the buffer. This should always be the case. + if (bytes_transferred != bytes_processed) { + m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP response parser failed to consume all bytes from a read request."); + this->terminate(make_error_code(error::general)); + return; + } + + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_http_response, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } + return; + } lib::error_code validate_ec = m_processor->validate_server_handshake_response( m_request, @@ -1822,6 +2040,21 @@ void connection::handle_close_handshake_timeout( } } +template +void connection::handle_read_response_timeout(lib::error_code const & ec) +{ + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel,"asio read http response timer cancelled"); + } else if (ec) { + m_alog->write(log::alevel::devel, + "asio handle_read_response_timeout error: "+ec.message()); + // TODO: ignore or fail here? + } else { + m_alog->write(log::alevel::devel, "asio read http response timer expired"); + terminate(make_error_code(error::http_read_response_timeout)); + } +} + template void connection::terminate(lib::error_code const & ec) { if (m_alog->static_test(log::alevel::devel)) { @@ -1837,7 +2070,7 @@ void connection::terminate(lib::error_code const & ec) { // Cancel ping timer if (m_ping_timer) { m_ping_timer->cancel(); - m_handshake_timer.reset(); + m_ping_timer.reset(); } terminate_status tstat = unknown; @@ -1848,8 +2081,10 @@ void connection::terminate(lib::error_code const & ec) { } // TODO: does any of this need a mutex? - if (m_is_http) { + if (m_is_http && m_http_state != session::http_state::closed) { m_http_state = session::http_state::closed; + if (!m_is_server && m_http_handler) // only call m_http_handler here for client connections + m_http_handler(m_connection_hdl); } if (m_state == session::state::connecting) { m_state = session::state::closed; diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp index e945a78f3..ba84c3a70 100644 --- a/websocketpp/impl/endpoint_impl.hpp +++ b/websocketpp/impl/endpoint_impl.hpp @@ -67,11 +67,16 @@ endpoint::create_connection(lib::error_code & ec) { con->set_validate_handler(m_validate_handler); con->set_message_handler(m_message_handler); + con->set_max_redirects(m_max_redirects); + if (m_open_handshake_timeout_dur != config::timeout_open_handshake) { con->set_open_handshake_timeout(m_open_handshake_timeout_dur); } if (m_close_handshake_timeout_dur != config::timeout_close_handshake) { con->set_close_handshake_timeout(m_close_handshake_timeout_dur); + } + if (m_http_read_timeout_dur != config::timeout_read_http_response) { + con->set_http_response_timeout(m_http_read_timeout_dur); } if (m_pong_timeout_dur != config::timeout_pong) { con->set_pong_timeout(m_pong_timeout_dur); @@ -91,7 +96,7 @@ endpoint::create_connection(lib::error_code & ec) { } template -void endpoint::interrupt(connection_hdl hdl, lib::error_code & ec) +void endpoint::interrupt(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} @@ -102,7 +107,7 @@ void endpoint::interrupt(connection_hdl hdl, lib::error_code } template -void endpoint::pause_reading(connection_hdl hdl, lib::error_code & ec) +void endpoint::pause_reading(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} @@ -111,7 +116,7 @@ void endpoint::pause_reading(connection_hdl hdl, lib::error_c } template -void endpoint::resume_reading(connection_hdl hdl, lib::error_code & ec) +void endpoint::resume_reading(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} @@ -120,7 +125,7 @@ void endpoint::resume_reading(connection_hdl hdl, lib::error_ } template -void endpoint::send_http_response(connection_hdl hdl, +void endpoint::send_http_response(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -129,7 +134,7 @@ void endpoint::send_http_response(connection_hdl hdl, } template -void endpoint::send(connection_hdl hdl, std::string const & payload, +void endpoint::send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -139,7 +144,7 @@ void endpoint::send(connection_hdl hdl, std::string const & p } template -void endpoint::send(connection_hdl hdl, void const * payload, +void endpoint::send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -148,7 +153,7 @@ void endpoint::send(connection_hdl hdl, void const * payload, } template -void endpoint::send(connection_hdl hdl, message_ptr msg, +void endpoint::send(connection_hdl_ref hdl, message_ptr msg, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -157,7 +162,7 @@ void endpoint::send(connection_hdl hdl, message_ptr msg, } template -void endpoint::close(connection_hdl hdl, close::status::value +void endpoint::close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason, lib::error_code & ec) { @@ -167,7 +172,7 @@ void endpoint::close(connection_hdl hdl, close::status::value } template -void endpoint::ping(connection_hdl hdl, std::string const & +void endpoint::ping(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -176,7 +181,7 @@ void endpoint::ping(connection_hdl hdl, std::string const & } template -void endpoint::pong(connection_hdl hdl, std::string const & payload, +void endpoint::pong(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -188,35 +193,35 @@ void endpoint::pong(connection_hdl hdl, std::string const & p // If exceptions are enabled, define wrapper methods that throw exceptions template -void endpoint::interrupt(connection_hdl hdl) { +void endpoint::interrupt(connection_hdl_ref hdl) { lib::error_code ec; interrupt(hdl,ec); if (ec) { throw exception(ec); } } template -void endpoint::pause_reading(connection_hdl hdl) { +void endpoint::pause_reading(connection_hdl_ref hdl) { lib::error_code ec; pause_reading(hdl,ec); if (ec) { throw exception(ec); } } template -void endpoint::resume_reading(connection_hdl hdl) { +void endpoint::resume_reading(connection_hdl_ref hdl) { lib::error_code ec; resume_reading(hdl,ec); if (ec) { throw exception(ec); } } template -void endpoint::send_http_response(connection_hdl hdl) { +void endpoint::send_http_response(connection_hdl_ref 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, +void endpoint::send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op) { lib::error_code ec; @@ -225,7 +230,7 @@ void endpoint::send(connection_hdl hdl, std::string const & p } template -void endpoint::send(connection_hdl hdl, void const * payload, +void endpoint::send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op) { lib::error_code ec; @@ -234,14 +239,14 @@ void endpoint::send(connection_hdl hdl, void const * payload, } template -void endpoint::send(connection_hdl hdl, message_ptr msg) { +void endpoint::send(connection_hdl_ref hdl, message_ptr msg) { lib::error_code ec; send(hdl,msg,ec); if (ec) { throw exception(ec); } } template -void endpoint::close(connection_hdl hdl, close::status::value +void endpoint::close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason) { lib::error_code ec; @@ -250,7 +255,7 @@ void endpoint::close(connection_hdl hdl, close::status::value } template -void endpoint::ping(connection_hdl hdl, std::string const & payload) +void endpoint::ping(connection_hdl_ref hdl, std::string const & payload) { lib::error_code ec; ping(hdl,payload,ec); @@ -258,7 +263,7 @@ void endpoint::ping(connection_hdl hdl, std::string const & p } template -void endpoint::pong(connection_hdl hdl, std::string const & payload) +void endpoint::pong(connection_hdl_ref hdl, std::string const & payload) { lib::error_code ec; pong(hdl,payload,ec); diff --git a/websocketpp/logger/levels.hpp b/websocketpp/logger/levels.hpp index cd7ccd690..4adce8e94 100644 --- a/websocketpp/logger/levels.hpp +++ b/websocketpp/logger/levels.hpp @@ -48,11 +48,11 @@ struct channel_type_hint { typedef uint32_t value; /// No information - static value const none = 0; + static constexpr value none = 0; /// Access log - static value const access = 1; + static constexpr value access = 1; /// Error log - static value const error = 2; + static constexpr value error = 2; }; /// Package of log levels for logging errors diff --git a/websocketpp/logger/stub.hpp b/websocketpp/logger/stub.hpp index 2db6da7df..e805a7653 100644 --- a/websocketpp/logger/stub.hpp +++ b/websocketpp/logger/stub.hpp @@ -44,14 +44,14 @@ class stub { /** * @param hint A channel type specific hint for how to construct the logger */ - explicit stub(channel_type_hint::value) {} + explicit stub(channel_type_hint) {} /// Construct the logger /** * @param default_channels A set of channels to statically enable * @param hint A channel type specific hint for how to construct the logger */ - stub(level, channel_type_hint::value) {} + stub(level, channel_type_hint) {} _WEBSOCKETPP_CONSTEXPR_TOKEN_ stub() {} /// Dynamically enable the given list of channels diff --git a/websocketpp/logger/syslog.hpp b/websocketpp/logger/syslog.hpp index 513abee4a..96fbd0d89 100644 --- a/websocketpp/logger/syslog.hpp +++ b/websocketpp/logger/syslog.hpp @@ -51,7 +51,7 @@ class syslog : public basic { /** * @param hint A channel type specific hint for how to construct the logger */ - syslog(channel_type_hint::value hint = + syslog(channel_type_hint hint = channel_type_hint::access) : basic(hint), m_channel_type_hint(hint) {} @@ -60,7 +60,7 @@ class syslog : public basic { * @param channels A set of channels to statically enable * @param hint A channel type specific hint for how to construct the logger */ - syslog(level channels, channel_type_hint::value hint = + syslog(level channels, channel_type_hint hint = channel_type_hint::access) : basic(channels, hint), m_channel_type_hint(hint) {} @@ -137,7 +137,7 @@ class syslog : public basic { return default_level; } - channel_type_hint::value m_channel_type_hint; + channel_type_hint m_channel_type_hint; }; } // log diff --git a/websocketpp/processors/hybi00.hpp b/websocketpp/processors/hybi00.hpp index 173eff05b..0cd51fc09 100644 --- a/websocketpp/processors/hybi00.hpp +++ b/websocketpp/processors/hybi00.hpp @@ -177,9 +177,21 @@ class hybi00 : public processor { } std::string get_raw(response_type const & res) const { - response_type temp = res; - temp.remove_header("Sec-WebSocket-Key3"); - return temp.raw() + res.get_header("Sec-WebSocket-Key3"); + // TODO: validation. Make sure all required fields have been set? + std::stringstream ret; + + ret << get_version() << " " << res.get_status_code() << " " << res.get_status_msg() << "\r\n"; + for (const auto& header : res.get_headers()) { + if (header.first != "Sec-WebSocket-Key3") + ret << header.first << ": " << header.second << "\r\n"; + } + ret << "\r\n"; + + ret << res.get_body(); + + ret << res.get_header("Sec-WebSocket-Key3"); + + return ret.str(); } std::string const & get_origin(request_type const & r) const { @@ -228,9 +240,10 @@ class hybi00 : public processor { if (last_colon == std::string::npos || (last_sbrace != std::string::npos && last_sbrace > last_colon)) { - return lib::make_shared(base::m_secure, h, request.get_uri()); + return lib::make_shared(uri::websocket, base::m_secure, + h, request.get_uri()); } else { - return lib::make_shared(base::m_secure, + return lib::make_shared(uri::websocket, base::m_secure, h.substr(0,last_colon), h.substr(last_colon+1), request.get_uri()); diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp index 91813f620..beca4b915 100644 --- a/websocketpp/processors/hybi13.hpp +++ b/websocketpp/processors/hybi13.hpp @@ -349,7 +349,7 @@ class hybi13 : public processor { } uri_ptr get_uri(request_type const & request) const { - return get_uri_from_host(request,(base::m_secure ? "wss" : "ws")); + return get_uri_from_host(request, uri::websocket, base::m_secure); } /// Process new websocket connection bytes diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp index 5131cc45e..eaf4cf0b0 100644 --- a/websocketpp/processors/processor.hpp +++ b/websocketpp/processors/processor.hpp @@ -133,11 +133,18 @@ int get_websocket_version(request_type& r) { * @return A uri_pointer that encodes the value of the host header. */ template -uri_ptr get_uri_from_host(request_type & request, std::string scheme) { +uri_ptr get_uri_from_host(request_type & request, uri::type scheme, bool secure) { std::string h = request.get_header("Host"); size_t last_colon = h.rfind(":"); size_t last_sbrace = h.rfind("]"); + std::string port, hostname; + if (last_sbrace != std::string::npos) + { + hostname = h.substr(1, last_sbrace - 1); + } else { + hostname = h.substr(0, last_colon); + } // no : = hostname with no port // last : before ] = ipv6 literal with no port @@ -146,10 +153,10 @@ uri_ptr get_uri_from_host(request_type & request, std::string scheme) { if (last_colon == std::string::npos || (last_sbrace != std::string::npos && last_sbrace > last_colon)) { - return lib::make_shared(scheme, h, request.get_uri()); + return lib::make_shared(scheme, secure, hostname, request.get_uri()); } else { - return lib::make_shared(scheme, - h.substr(0,last_colon), + return lib::make_shared(scheme, secure, + hostname, h.substr(last_colon+1), request.get_uri()); } diff --git a/websocketpp/roles/server_endpoint.hpp b/websocketpp/roles/server_endpoint.hpp index 04fee18f9..b69281fdb 100644 --- a/websocketpp/roles/server_endpoint.hpp +++ b/websocketpp/roles/server_endpoint.hpp @@ -137,8 +137,8 @@ class server : public endpoint,config> { /// Starts the server's async connection acceptance loop (exception free) /** * Initiates the server connection acceptance loop. Must be called after - * listen. This method will have no effect until the underlying io_service - * starts running. It may be called after the io_service is already running. + * listen. This method will have no effect until the underlying io_context + * starts running. It may be called after the io_context is already running. * * Refer to documentation for the transport policy you are using for * instructions on how to stop this acceptance loop. diff --git a/websocketpp/transport/asio/base.hpp b/websocketpp/transport/asio/base.hpp index b945fe11f..8d980ad7c 100644 --- a/websocketpp/transport/asio/base.hpp +++ b/websocketpp/transport/asio/base.hpp @@ -40,7 +40,7 @@ namespace websocketpp { namespace transport { /// Transport policy that uses asio /** - * This policy uses a single asio io_service to provide transport + * This policy uses a single asio io_context to provide transport * services to a WebSocket++ endpoint. */ namespace asio { diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp index ea18e3e6d..c04f7895a 100644 --- a/websocketpp/transport/asio/connection.hpp +++ b/websocketpp/transport/asio/connection.hpp @@ -85,10 +85,10 @@ 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 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 io_context being used + typedef lib::asio::io_context * io_context_ptr; + /// Type of a pointer to the Asio io_context::strand being used + typedef lib::shared_ptr strand_ptr; /// Type of a pointer to the Asio timer class typedef lib::shared_ptr timer_ptr; @@ -97,7 +97,7 @@ class connection : public config::socket_type::socket_con_type { // to the public api. friend class endpoint; - // generate and manage our own io_service + // generate and manage our own io_context explicit connection(bool is_server, const lib::shared_ptr & alog, const lib::shared_ptr & elog) : m_is_server(is_server) , m_alog(alog) @@ -106,6 +106,8 @@ class connection : public config::socket_type::socket_con_type { m_alog->write(log::alevel::devel,"asio con transport constructor"); } + virtual ~connection() {} + /// Get a shared pointer to this component ptr get_shared() { return lib::static_pointer_cast(socket_con_type::get_shared()); @@ -319,7 +321,7 @@ class connection : public config::socket_type::socket_con_type { timer_ptr set_timer(long duration, timer_handler callback) { timer_ptr new_timer( new lib::asio::steady_timer( - *m_io_service, + *m_io_context, lib::asio::milliseconds(duration)) ); @@ -399,7 +401,7 @@ class connection : public config::socket_type::socket_con_type { /// Initialize transport for reading /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service + * Asio components to the io_context * * The transport initialization sequence consists of the following steps: * - Pre-init: the underlying socket is initialized to the point where @@ -457,21 +459,21 @@ class connection : public config::socket_type::socket_con_type { /// Finish constructing the transport /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service. + * Asio components to the io_context. * - * @param io_service A pointer to the io_service to register with this + * @param io_context A pointer to the io_context to register with this * connection * * @return Status code for the success or failure of the initialization */ - lib::error_code init_asio (io_service_ptr io_service) { - m_io_service = io_service; + lib::error_code init_asio (io_context_ptr io_context) { + m_io_context = io_context; if (config::enable_multithreading) { - m_strand.reset(new lib::asio::io_service::strand(*io_service)); + m_strand.reset(new lib::asio::io_context::strand(*io_context)); } - lib::error_code ec = socket_con_type::init_asio(io_service, m_strand, + lib::error_code ec = socket_con_type::init_asio(io_context, m_strand, m_is_server); return ec; @@ -791,7 +793,7 @@ class connection : public config::socket_type::socket_con_type { return; } - if (!m_proxy_data->res.headers_ready()) { + if (!m_proxy_data->res.has_received(response_type::state::HEADERS)) { // we read until the headers were done in theory but apparently // they aren't. Internal endpoint error. callback(make_error_code(error::general)); @@ -1019,7 +1021,7 @@ class connection : public config::socket_type::socket_con_type { * @param hdl A connection_hdl that the transport will use to refer * to itself */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_connection_hdl = hdl; socket_con_type::set_handle(hdl); } @@ -1030,18 +1032,18 @@ class connection : public config::socket_type::socket_con_type { */ lib::error_code interrupt(interrupt_handler handler) { if (config::enable_multithreading) { - m_io_service->post(m_strand->wrap(handler)); + m_io_context->post(m_strand->wrap(handler)); } else { - m_io_service->post(handler); + m_io_context->post(handler); } return lib::error_code(); } lib::error_code dispatch(dispatch_handler handler) { if (config::enable_multithreading) { - m_io_service->post(m_strand->wrap(handler)); + m_io_context->post(m_strand->wrap(handler)); } else { - m_io_service->post(handler); + m_io_context->post(handler); } return lib::error_code(); } @@ -1190,7 +1192,7 @@ class connection : public config::socket_type::socket_con_type { lib::shared_ptr m_proxy_data; // transport resources - io_service_ptr m_io_service; + io_context_ptr m_io_context; strand_ptr m_strand; connection_hdl m_connection_hdl; diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp index 125b87642..03ccbb8b2 100644 --- a/websocketpp/transport/asio/endpoint.hpp +++ b/websocketpp/transport/asio/endpoint.hpp @@ -77,24 +77,24 @@ class endpoint : public config::socket_type { /// associated with this endpoint transport component typedef typename transport_con_type::ptr transport_con_ptr; - /// Type of a pointer to the ASIO io_service being used - typedef lib::asio::io_service * io_service_ptr; + /// Type of a pointer to the ASIO io_context being used + typedef lib::asio::io_context * io_context_ptr; /// Type of a shared pointer to the acceptor being used typedef lib::shared_ptr acceptor_ptr; /// Type of a shared pointer to the resolver being used typedef lib::shared_ptr resolver_ptr; /// Type of timer handle typedef lib::shared_ptr timer_ptr; - /// Type of a shared pointer to an io_service work object - typedef lib::shared_ptr work_ptr; + /// Type of a shared pointer to an io_context work object + typedef lib::shared_ptr work_ptr; /// Type of socket pre-bind handler typedef lib::function tcp_pre_bind_handler; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() - : m_io_service(NULL) - , m_external_io_service(false) + : m_io_context(NULL) + , m_external_io_context(false) , m_listen_backlog(lib::asio::socket_base::max_connections) , m_reuse_addr(false) , m_state(UNINITIALIZED) @@ -102,15 +102,15 @@ class endpoint : public config::socket_type { //std::cout << "transport::asio::endpoint constructor" << std::endl; } - ~endpoint() { - // clean up our io_service if we were initialized with an internal one. + virtual ~endpoint() { + // clean up our io_context if we were initialized with an internal one. // Explicitly destroy local objects m_acceptor.reset(); m_resolver.reset(); m_work.reset(); - if (m_state != UNINITIALIZED && !m_external_io_service) { - delete m_io_service; + if (m_state != UNINITIALIZED && !m_external_io_context) { + delete m_io_context; } } @@ -132,8 +132,8 @@ class endpoint : public config::socket_type { : 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_io_context(src.m_io_context) + , m_external_io_context(src.m_external_io_context) , m_acceptor(src.m_acceptor) , m_listen_backlog(lib::asio::socket_base::max_connections) , m_reuse_addr(src.m_reuse_addr) @@ -141,23 +141,23 @@ class endpoint : public config::socket_type { , m_alog(src.m_alog) , m_state(src.m_state) { - src.m_io_service = NULL; - src.m_external_io_service = false; + src.m_io_context = NULL; + src.m_external_io_context = false; src.m_acceptor = NULL; src.m_state = UNINITIALIZED; } /*endpoint & operator= (const endpoint && rhs) { if (this != &rhs) { - m_io_service = rhs.m_io_service; - m_external_io_service = rhs.m_external_io_service; + m_io_context = rhs.m_io_context; + m_external_io_context = rhs.m_external_io_context; m_acceptor = rhs.m_acceptor; m_listen_backlog = rhs.m_listen_backlog; m_reuse_addr = rhs.m_reuse_addr; m_state = rhs.m_state; - rhs.m_io_service = NULL; - rhs.m_external_io_service = false; + rhs.m_io_context = NULL; + rhs.m_external_io_context = false; rhs.m_acceptor = NULL; rhs.m_listen_backlog = lib::asio::socket_base::max_connections; rhs.m_state = UNINITIALIZED; @@ -173,16 +173,16 @@ class endpoint : public config::socket_type { return socket_type::is_secure(); } - /// initialize asio transport with external io_service (exception free) + /// initialize asio transport with external io_context (exception free) /** * Initialize the ASIO transport policy for this endpoint using the provided - * io_service object. asio_init must be called exactly once on any endpoint + * io_context object. asio_init must be called exactly once on any endpoint * that uses transport::asio before it can be used. * - * @param ptr A pointer to the io_service to use for asio events + * @param ptr A pointer to the io_context to use for asio events * @param ec Set to indicate what error occurred, if any. */ - void init_asio(io_service_ptr ptr, lib::error_code & ec) { + void init_asio(io_context_ptr ptr, lib::error_code & ec) { if (m_state != UNINITIALIZED) { m_elog->write(log::elevel::library, "asio::init_asio called from the wrong state"); @@ -193,36 +193,36 @@ class endpoint : public config::socket_type { m_alog->write(log::alevel::devel,"asio::init_asio"); - m_io_service = ptr; - m_external_io_service = true; - m_acceptor.reset(new lib::asio::ip::tcp::acceptor(*m_io_service)); + m_io_context = ptr; + m_external_io_context = true; + m_acceptor.reset(new lib::asio::ip::tcp::acceptor(*m_io_context)); m_state = READY; ec = lib::error_code(); } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ - /// initialize asio transport with external io_service + /// initialize asio transport with external io_context /** * Initialize the ASIO transport policy for this endpoint using the provided - * io_service object. asio_init must be called exactly once on any endpoint + * io_context object. asio_init must be called exactly once on any endpoint * that uses transport::asio before it can be used. * - * @param ptr A pointer to the io_service to use for asio events + * @param ptr A pointer to the io_context to use for asio events */ - void init_asio(io_service_ptr ptr) { + void init_asio(io_context_ptr ptr) { lib::error_code ec; init_asio(ptr,ec); if (ec) { throw exception(ec); } } #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Initialize asio transport with internal io_service (exception free) + /// Initialize asio transport with internal io_context (exception free) /** * This method of initialization will allocate and use an internally managed - * io_service. + * io_context. * - * @see init_asio(io_service_ptr ptr) + * @see init_asio(io_context_ptr ptr) * * @param ec Set to indicate what error occurred, if any. */ @@ -232,22 +232,22 @@ class endpoint : public config::socket_type { // TODO: remove the use of auto_ptr when C++98/03 support is no longer // necessary. #ifdef _WEBSOCKETPP_CPP11_MEMORY_ - lib::unique_ptr service(new lib::asio::io_service()); + lib::unique_ptr service(new lib::asio::io_context()); #else - lib::auto_ptr service(new lib::asio::io_service()); + lib::auto_ptr service(new lib::asio::io_context()); #endif init_asio(service.get(), ec); if( !ec ) service.release(); // Call was successful, transfer ownership - m_external_io_service = false; + m_external_io_context = false; } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Initialize asio transport with internal io_service + /// Initialize asio transport with internal io_context /** * This method of initialization will allocate and use an internally managed - * io_service. + * io_context. * - * @see init_asio(io_service_ptr ptr) + * @see init_asio(io_context_ptr ptr) */ void init_asio() { // Use a smart pointer until the call is successful and ownership has @@ -255,14 +255,14 @@ class endpoint : public config::socket_type { // TODO: remove the use of auto_ptr when C++98/03 support is no longer // necessary. #ifdef _WEBSOCKETPP_CPP11_MEMORY_ - lib::unique_ptr service(new lib::asio::io_service()); + lib::unique_ptr service(new lib::asio::io_context()); #else - lib::auto_ptr service(new lib::asio::io_service()); + lib::auto_ptr service(new lib::asio::io_context()); #endif init_asio( service.get() ); // If control got this far without an exception, then ownership has successfully been taken service.release(); - m_external_io_service = false; + m_external_io_context = false; } #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ @@ -368,19 +368,19 @@ class endpoint : public config::socket_type { m_reuse_addr = value; } - /// Retrieve a reference to the endpoint's io_service + /// Retrieve a reference to the endpoint's io_context /** - * The io_service may be an internal or external one. This may be used to - * call methods of the io_service that are not explicitly wrapped by the + * The io_context may be an internal or external one. This may be used to + * call methods of the io_context that are not explicitly wrapped by the * endpoint. * * This method is only valid after the endpoint has been initialized with * `init_asio`. No error will be returned if it isn't. * - * @return A reference to the endpoint's io_service + * @return A reference to the endpoint's io_context */ - lib::asio::io_service & get_io_service() { - return *m_io_service; + lib::asio::io_context & get_io_context() { + return *m_io_context; } /// Get local TCP endpoint @@ -501,7 +501,7 @@ class endpoint : public config::socket_type { * The endpoint must have been initialized by calling init_asio before * listening. * - * Once listening the underlying io_service will be kept open indefinitely. + * Once listening the underlying io_context will be kept open indefinitely. * Calling endpoint::stop_listening will stop the endpoint from accepting * new connections. See the documentation for stop listening for more details * about shutting down Asio Transport based endpoints. @@ -518,7 +518,7 @@ class endpoint : public config::socket_type { lib::error_code & ec) { using lib::asio::ip::tcp; - tcp::resolver r(*m_io_service); + tcp::resolver r(*m_io_context); tcp::resolver::query query(host, service); tcp::resolver::iterator endpoint_iterator = r.resolve(query); tcp::resolver::iterator end; @@ -621,7 +621,7 @@ class endpoint : public config::socket_type { * The endpoint must have been initialized by calling init_asio before * listening. * - * Once listening the underlying io_service will be kept open indefinitely. + * Once listening the underlying io_context will be kept open indefinitely. * Calling endpoint::stop_listening will stop the endpoint from accepting * new connections. See the documentation for stop listening for more * details about shutting down Asio Transport based endpoints. @@ -663,42 +663,42 @@ class endpoint : public config::socket_type { return (m_state == LISTENING); } - /// wraps the run method of the internal io_service object + /// wraps the run method of the internal io_context object std::size_t run() { - return m_io_service->run(); + return m_io_context->run(); } - /// wraps the run_one method of the internal io_service object + /// wraps the run_one method of the internal io_context object /** * @since 0.3.0-alpha4 */ std::size_t run_one() { - return m_io_service->run_one(); + return m_io_context->run_one(); } - /// wraps the stop method of the internal io_service object + /// wraps the stop method of the internal io_context object void stop() { - m_io_service->stop(); + m_io_context->stop(); } - /// wraps the poll method of the internal io_service object + /// wraps the poll method of the internal io_context object std::size_t poll() { - return m_io_service->poll(); + return m_io_context->poll(); } - /// wraps the poll_one method of the internal io_service object + /// wraps the poll_one method of the internal io_context object std::size_t poll_one() { - return m_io_service->poll_one(); + return m_io_context->poll_one(); } - /// wraps the reset method of the internal io_service object + /// wraps the reset method of the internal io_context object void reset() { - m_io_service->reset(); + m_io_context->reset(); } - /// wraps the stopped method of the internal io_service object + /// wraps the stopped method of the internal io_context object bool stopped() const { - return m_io_service->stopped(); + return m_io_context->stopped(); } /// Marks the endpoint as perpetual, stopping it from exiting when empty @@ -714,7 +714,7 @@ class endpoint : public config::socket_type { * @since 0.3.0 */ void start_perpetual() { - m_work.reset(new lib::asio::io_service::work(*m_io_service)); + m_work.reset(new lib::asio::io_context::work(*m_io_context)); } /// Clears the endpoint's perpetual flag, allowing it to exit when empty @@ -743,7 +743,7 @@ class endpoint : public config::socket_type { */ timer_ptr set_timer(long duration, timer_handler callback) { timer_ptr new_timer = lib::make_shared( - *m_io_service, + *m_io_context, lib::asio::milliseconds(duration) ); @@ -880,7 +880,7 @@ class endpoint : public config::socket_type { // Create a resolver if (!m_resolver) { - m_resolver.reset(new lib::asio::ip::tcp::resolver(*m_io_service)); + m_resolver.reset(new lib::asio::ip::tcp::resolver(*m_io_context)); } tcon->set_uri(u); @@ -912,21 +912,16 @@ class endpoint : public config::socket_type { port = pu->get_port_str(); } - tcp::resolver::query query(host,port); - if (m_alog->static_test(log::alevel::devel)) { m_alog->write(log::alevel::devel, "starting async DNS resolve for "+host+":"+port); } - timer_ptr dns_timer; - - dns_timer = tcon->set_timer( + timer_ptr dns_timer = tcon->set_timer( config::timeout_dns_resolve, lib::bind( &type::handle_resolve_timeout, this, - dns_timer, cb, lib::placeholders::_1 ) @@ -934,7 +929,7 @@ class endpoint : public config::socket_type { if (config::enable_multithreading) { m_resolver->async_resolve( - query, + host, port, tcon->get_strand()->wrap(lib::bind( &type::handle_resolve, this, @@ -947,7 +942,7 @@ class endpoint : public config::socket_type { ); } else { m_resolver->async_resolve( - query, + host, port, lib::bind( &type::handle_resolve, this, @@ -970,7 +965,7 @@ class endpoint : public config::socket_type { * @param callback The function to call back * @param ec A status code indicating an error, if any. */ - void handle_resolve_timeout(timer_ptr, connect_handler callback, + void handle_resolve_timeout(connect_handler callback, lib::error_code const & ec) { lib::error_code ret_ec; @@ -985,7 +980,7 @@ class endpoint : public config::socket_type { log_err(log::elevel::devel,"asio handle_resolve_timeout",ec); ret_ec = ec; } else { - ret_ec = make_error_code(transport::error::timeout); + ret_ec = make_error_code(transport::error::resolve_failed); } m_alog->write(log::alevel::devel,"DNS resolution timed out"); @@ -993,7 +988,7 @@ class endpoint : public config::socket_type { callback(ret_ec); } - void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer, + void handle_resolve(transport_con_ptr tcon, const timer_ptr& dns_timer, connect_handler callback, lib::asio::error_code const & ec, lib::asio::ip::tcp::resolver::iterator iterator) { @@ -1148,7 +1143,7 @@ class endpoint : public config::socket_type { lib::error_code ec; - ec = tcon->init_asio(m_io_service); + ec = tcon->init_asio(m_io_context); if (ec) {return ec;} tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler); @@ -1187,8 +1182,8 @@ class endpoint : public config::socket_type { tcp_init_handler m_tcp_post_init_handler; // Network Resources - io_service_ptr m_io_service; - bool m_external_io_service; + io_context_ptr m_io_context; + bool m_external_io_context; acceptor_ptr m_acceptor; resolver_ptr m_resolver; work_ptr m_work; diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp index c679378ed..1c564732d 100644 --- a/websocketpp/transport/asio/security/none.hpp +++ b/websocketpp/transport/asio/security/none.hpp @@ -62,10 +62,10 @@ 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 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 io_context being used + typedef lib::asio::io_context* io_context_ptr; + /// Type of a pointer to the Asio io_context strand being used + typedef lib::shared_ptr strand_ptr; /// Type of the ASIO socket being used typedef lib::asio::ip::tcp::socket socket_type; /// Type of a shared pointer to the socket being used. @@ -125,6 +125,10 @@ class connection : public lib::enable_shared_from_this { return *m_socket; } + const lib::asio::ip::tcp::socket & get_raw_socket() const { + return *m_socket; + } + /// Get the remote endpoint address /** * The iostream transport has no information about the ultimate remote @@ -156,14 +160,14 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service. At this stage the connection is + * Asio components to the io_context. At this stage the connection is * speculative, the server may not have actually received a new connection. * - * @param service A pointer to the endpoint's io_service + * @param service A pointer to the endpoint's io_context * @param strand A shared pointer to the connection's asio strand * @param is_server Whether or not the endpoint is a server or not. */ - lib::error_code init_asio (io_service_ptr service, strand_ptr, bool) + lib::error_code init_asio (io_context_ptr service, strand_ptr, bool) { if (m_state != UNINITIALIZED) { return socket::make_error_code(socket::error::invalid_state); @@ -232,7 +236,7 @@ class connection : public lib::enable_shared_from_this { * * @param hdl The new handle */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_hdl = hdl; } diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index 25b0d29d0..3c0799dc1 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -52,7 +52,7 @@ namespace tls_socket { 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_ref)> tls_init_handler; /// TLS enabled Asio connection socket component @@ -71,10 +71,10 @@ class connection : public lib::enable_shared_from_this { 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 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 io_context being used + typedef lib::asio::io_context * io_context_ptr; + /// Type of a pointer to the ASIO io_context strand being used + typedef lib::shared_ptr strand_ptr; /// Type of a shared pointer to the ASIO TLS context being used typedef lib::shared_ptr context_ptr; @@ -104,6 +104,10 @@ class connection : public lib::enable_shared_from_this { return m_socket->lowest_layer(); } + const socket_type::lowest_layer_type & get_raw_socket() const { + return m_socket->lowest_layer(); + } + /// Retrieve a pointer to the layer below the ssl stream /** * This is used internally. @@ -176,13 +180,13 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service + * Asio components to the io_context * - * @param service A pointer to the endpoint's io_service + * @param service A pointer to the endpoint's io_context * @param strand A pointer to the connection's strand * @param is_server Whether or not the endpoint is a server or not. */ - lib::error_code init_asio (io_service_ptr service, strand_ptr strand, + lib::error_code init_asio (io_context_ptr service, strand_ptr strand, bool is_server) { if (!m_tls_init_handler) { @@ -195,7 +199,7 @@ class connection : public lib::enable_shared_from_this { } m_socket.reset(new socket_type(*service, *m_context)); - m_io_service = service; + m_io_context = service; m_strand = strand; m_is_server = is_server; @@ -238,8 +242,12 @@ class connection : public lib::enable_shared_from_this { std::string const & host = m_uri->get_host(); lib::asio::error_code ec_addr; - // run the hostname through make_address to check if it is a valid IP literal - lib::asio::ip::address addr = lib::asio::ip::make_address(host, ec_addr); + // run the hostname through make_address to check if it is a valid IP literal +#if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) < 66 + lib::asio::ip::address::from_string(host, ec_addr); +#else + lib::asio::ip::make_address(host, ec_addr); +#endif // BOOST_VERSION // If the parsing as an IP literal fails, proceed to register the hostname // with the TLS handshake via SNI. @@ -250,6 +258,7 @@ class connection : public lib::enable_shared_from_this { get_socket().native_handle(), host.c_str()); if (!(1 == res)) { callback(socket::make_error_code(socket::error::tls_failed_sni_hostname)); + return; } } } @@ -301,7 +310,7 @@ class connection : public lib::enable_shared_from_this { * * @param hdl The new handle */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_hdl = hdl; } @@ -335,6 +344,7 @@ class connection : public lib::enable_shared_from_this { } void async_shutdown(socket::shutdown_handler callback) { + callback = lib::bind(&connection::handle_async_shutdown, this, callback, lib::placeholders::_1); if (m_strand) { m_socket->async_shutdown(m_strand->wrap(callback)); } else { @@ -342,6 +352,28 @@ class connection : public lib::enable_shared_from_this { } } + void handle_async_shutdown(socket::shutdown_handler callback, lib::asio::error_code const & ec) + { + lib::asio::error_code code { ec }; + if (ec == lib::asio::error::operation_aborted) + { + const int shutdownState = SSL_get_shutdown(get_socket().native_handle()); + if (shutdownState & SSL_RECEIVED_SHUTDOWN) + { + code = lib::asio::error_code(lib::asio::error::not_connected, ec.category()); + } + } + + if (ec == lib::asio::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + code.clear(); + } + + callback(code); + } + public: /// Translate any security policy specific information about an error code /** @@ -391,7 +423,7 @@ class connection : public lib::enable_shared_from_this { } } - io_service_ptr m_io_service; + io_context_ptr m_io_context; strand_ptr m_strand; context_ptr m_context; socket_ptr m_socket; diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp index f76d40913..0967c6469 100644 --- a/websocketpp/transport/base/connection.hpp +++ b/websocketpp/transport/base/connection.hpp @@ -75,7 +75,7 @@ namespace websocketpp { * write * * **set_handle**\n - * `void set_handle(connection_hdl hdl)`\n + * `void set_handle(connection_hdl_ref hdl)`\n * Called by WebSocket++ to let this policy know the hdl to the connection. It * may be stored for later use or ignored/discarded. This handle should be used * if the policy adds any connection handlers. Connection handlers must be @@ -173,6 +173,9 @@ enum value { /// Timer expired timeout, + /// Hostname resolution failed or timed out + resolve_failed, + /// read or write after shutdown action_after_shutdown, @@ -206,6 +209,8 @@ class category : public lib::error_category { return "TLS Short Read"; case timeout: return "Timer Expired"; + case resolve_failed: + return "Hostname resolution failed or timed out"; case action_after_shutdown: return "A transport action was requested after shutdown"; case tls_error: diff --git a/websocketpp/transport/debug/endpoint.hpp b/websocketpp/transport/debug/endpoint.hpp index adc89b382..360644165 100644 --- a/websocketpp/transport/debug/endpoint.hpp +++ b/websocketpp/transport/debug/endpoint.hpp @@ -60,7 +60,7 @@ class endpoint { /// associated connection transport component typedef typename transport_con_type::ptr transport_con_ptr; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() { //std::cout << "transport::iostream::endpoint constructor" << std::endl; diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp index 899a3e256..e22f8414a 100644 --- a/websocketpp/transport/iostream/connection.hpp +++ b/websocketpp/transport/iostream/connection.hpp @@ -562,7 +562,7 @@ class connection : public lib::enable_shared_from_this< connection > { /** * @param hdl The new handle */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_connection_hdl = hdl; } diff --git a/websocketpp/transport/iostream/endpoint.hpp b/websocketpp/transport/iostream/endpoint.hpp index 14cba7255..257472db8 100644 --- a/websocketpp/transport/iostream/endpoint.hpp +++ b/websocketpp/transport/iostream/endpoint.hpp @@ -64,7 +64,7 @@ class endpoint { /// associated connection transport component typedef typename transport_con_type::ptr transport_con_ptr; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() : m_output_stream(NULL), m_is_secure(false) { //std::cout << "transport::iostream::endpoint constructor" << std::endl; diff --git a/websocketpp/transport/stub/connection.hpp b/websocketpp/transport/stub/connection.hpp index 5bbbf0d79..410d9031b 100644 --- a/websocketpp/transport/stub/connection.hpp +++ b/websocketpp/transport/stub/connection.hpp @@ -247,7 +247,7 @@ class connection : public lib::enable_shared_from_this< connection > { /** * @param hdl The new handle */ - void set_handle(connection_hdl hdl) {} + void set_handle(connection_hdl_ref hdl) {} /// Call given handler back within the transport's event system (if present) /** diff --git a/websocketpp/transport/stub/endpoint.hpp b/websocketpp/transport/stub/endpoint.hpp index eb6570a4c..e0d5f2d3a 100644 --- a/websocketpp/transport/stub/endpoint.hpp +++ b/websocketpp/transport/stub/endpoint.hpp @@ -60,7 +60,7 @@ class endpoint { /// associated connection transport component typedef typename transport_con_type::ptr transport_con_ptr; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() { //std::cout << "transport::iostream::endpoint constructor" << std::endl; diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index b2e6ebaaf..aed61238f 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -47,7 +47,6 @@ static uint16_t const uri_default_port = 80; static uint16_t const uri_default_secure_port = 443; - /// A group of helper methods for parsing and validating URIs against RFC 3986 namespace uri_helper { @@ -263,7 +262,7 @@ inline bool ipv4_literal(std::string::const_iterator start, std::string::const_i } } } - + // check final octet return (counter == 3 && dec_octet(cursor,end)); } @@ -298,10 +297,11 @@ inline bool hex4(std::string::const_iterator start, std::string::const_iterator */ inline bool ipv6_literal(std::string::const_iterator start, std::string::const_iterator end) { // initial range check + end = std::find(start, end, '%'); if (end-start > 45 && end-start >= 2) { return false; } - + // peal off and count hex4s until we run out of colons, // note the abbreviation marker if we see one. std::string::const_iterator cursor = start; @@ -327,7 +327,7 @@ inline bool ipv6_literal(std::string::const_iterator start, std::string::const_i } it++; } - + // final bit either needs to be a hex4 or an IPv4 literal if (cursor == end) { // fine @@ -338,11 +338,11 @@ inline bool ipv6_literal(std::string::const_iterator start, std::string::const_i } else { return false; } - + if ((abbr == 0 && count != 8) || (abbr == 1 && count > 7) || abbr > 1) { return false; } - + return true; } @@ -389,6 +389,8 @@ inline bool reg_name(std::string::const_iterator start, std::string::const_itera return true; } +const std::string schema_separtator { "://" }; + } // end namespace uri_helper @@ -396,35 +398,60 @@ inline bool reg_name(std::string::const_iterator start, std::string::const_itera class uri { public: - explicit uri(std::string const & uri_string) : m_valid(false), m_ipv6_literal(false) { - std::string::const_iterator it; + enum type{ + invalid, + http, + websocket, + relative + }; + + uri() = default; + + explicit uri(std::string const & uri_string) : m_ipv6_literal(false) { + + if (uri_string.empty()) + return; + + const size_t schema_end = uri_string.find(uri_helper::schema_separtator); + if (schema_end != std::string::npos) + { + const std::string schema(uri_string.substr(0, schema_end)); + if (schema == "wss") + { + m_secure = true; + m_type = websocket; + } + else if (schema == "ws") + { + m_secure = false; + m_type = websocket; + } + else if (schema == "https") + { + m_secure = true; + m_type = http; + } + else if (schema == "http") + { + m_secure = false; + m_type = http; + } else { + return; + } + } else if (uri_string.starts_with('/')) { + m_type = relative; + m_resource = uri_string; + return; + } else { + return; // invalid! + } + + std::string::const_iterator it = uri_string.begin() + schema_end + uri_helper::schema_separtator.length(); std::string::const_iterator temp; int state = 0; - it = uri_string.begin(); - size_t uri_len = uri_string.length(); - - // extract scheme. We only consider Websocket and HTTP URI schemes as valid - if (uri_len >= 7 && std::equal(it,it+6,"wss://")) { - m_secure = true; - m_scheme = "wss"; - it += 6; - } else if (uri_len >= 6 && std::equal(it,it+5,"ws://")) { - m_secure = false; - m_scheme = "ws"; - it += 5; - } else if (uri_len >= 8 && std::equal(it,it+7,"http://")) { - m_secure = false; - m_scheme = "http"; - it += 7; - } else if (uri_len >= 9 && std::equal(it,it+8,"https://")) { - m_secure = true; - m_scheme = "https"; - it += 8; - } else { - return; - } + std::string schema; // extract host. // either a host string @@ -447,10 +474,12 @@ class uri { } if (temp == uri_string.end()) { + m_type = invalid; return; } else { // validate IPv6 literal parts if (!uri_helper::ipv6_literal(it,temp)) { + m_type = invalid; return; } else { m_ipv6_literal = true; @@ -463,8 +492,8 @@ class uri { } else if (*it == '/' || *it == '?' || *it == '#') { // todo: better path parsing state = 2; - - // we don't increment the iterator here because we want the + + // we don't increment the iterator here because we want the // delimiter to be read again as a part of the path } else if (*it == ':') { state = 1; @@ -473,6 +502,7 @@ class uri { ++it; } else { // problem + m_type = invalid; return; } } else { @@ -502,19 +532,20 @@ class uri { // end hostname and start parsing path state = 2; - // we don't increment the iterator here because we want the + // we don't increment the iterator here because we want the // delimiter to be read again as a part of the path } else { // either @, [, or ] // @ = userinfo fragment // [ and ] = illegal, basically + m_type = invalid; return; } } else { m_host += *it; ++it; } - + } } @@ -525,6 +556,7 @@ class uri { // if we stop parsing the port and there wasn't actually a port // we have an invalid URI if (port.empty()) { + m_type = invalid; return; } state = 3; @@ -535,62 +567,56 @@ class uri { // if we stop parsing the port and there wasn't actually a port // we have an invalid URI if (port.empty()) { + m_type = invalid; return; } state = 3; - // we don't increment the iterator here because we want the + // we don't increment the iterator here because we want the // delimiter to be read again as a part of the path } - + } lib::error_code ec; m_port = get_port_from_string(port, ec); if (ec) { + m_type = invalid; return; } // step back one so the first char of the path delimiter doesn't get eaten m_resource.append(it,uri_string.end()); - + if (m_resource.empty()) { m_resource = "/"; } // todo: validate path component - - - m_valid = true; } - uri(bool secure, std::string const & host, uint16_t port, + uri(type schema, bool secure, std::string const & host, uint16_t port, std::string const & resource) - : m_scheme(secure ? "wss" : "ws") + : m_type(schema) , m_host(host) , m_resource(resource.empty() ? "/" : resource) , m_port(port) , m_secure(secure) { m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); + if (!m_ipv6_literal && !uri_helper::reg_name(host.begin(), host.end())) + m_type = invalid; } - uri(bool secure, std::string const & host, std::string const & resource) - : m_scheme(secure ? "wss" : "ws") - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_port(secure ? uri_default_secure_port : uri_default_port) - , m_secure(secure) + uri(type schema, bool secure, std::string const & host, std::string const & resource) : + uri(schema, secure, host, secure ? uri_default_secure_port : uri_default_port, resource) { - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); } - uri(bool secure, std::string const & host, std::string const & port, + uri(type schema, bool secure, std::string const & host, std::string const & port, std::string const & resource) - : m_scheme(secure ? "wss" : "ws") + : m_type(schema) , m_host(host) , m_resource(resource.empty() ? "/" : resource) , m_secure(secure) @@ -599,50 +625,45 @@ class uri { m_port = get_port_from_string(port,ec); m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = !ec && (m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end())); + if (ec || !(m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()))) + m_type = invalid; } - uri(std::string const & scheme, std::string const & host, uint16_t port, - std::string const & resource) - : m_scheme(scheme) - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_port(port) - , m_secure(scheme == "wss" || scheme == "https") - { - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); - } - - uri(std::string scheme, std::string const & host, std::string const & resource) - : m_scheme(scheme) - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port) - , m_secure(scheme == "wss" || scheme == "https") - { - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); - } - - uri(std::string const & scheme, std::string const & host, - std::string const & port, std::string const & resource) - : m_scheme(scheme) - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_secure(scheme == "wss" || scheme == "https") + uri resource(const std::string& resource) const { - lib::error_code ec; - m_port = get_port_from_string(port,ec); - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - - m_valid = !ec && (m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end())); + uri ret = *this; + if (ret.get_resource().ends_with('/')) + { + if (resource.starts_with('/')) + ret.m_resource += resource.substr(1); + else + ret.m_resource += resource; + }else + { + if (resource.starts_with('/')) + ret.m_resource += resource; + else + ret.m_resource += "/" + resource; + } + return ret; } bool get_valid() const { - return m_valid; + return m_type != invalid; + } + + bool is_absolute() const { + return m_type == http || m_type == websocket; + } + + bool is_relative() const { + return m_type == relative; } + type get_type() const { + return m_type; + } + // Check whether the host of this URI is an IPv6 literal address /** * @since 0.8.3 @@ -656,8 +677,8 @@ class uri { return m_secure; } - std::string const & get_scheme() const { - return m_scheme; + std::string get_scheme() const { + return is_absolute() ? (m_type == http ? (m_secure ? "https" : "http") : (m_secure ? "wss" : "ws")) : std::string(); } std::string const & get_host() const { @@ -675,7 +696,7 @@ class uri { } else { p << m_host << ":" << m_port; } - + return p.str(); } } @@ -707,7 +728,7 @@ class uri { std::string str() const { std::stringstream s; - s << m_scheme << "://"; + s << get_scheme() << "://"; if (m_ipv6_literal) { s << "[" << m_host << "]"; } else { @@ -730,7 +751,7 @@ class uri { * @return query portion of the URI. */ std::string get_query() const { - std::size_t found = m_resource.find('?'); + const std::size_t found = m_resource.find('?'); if (found != std::string::npos) { return m_resource.substr(found + 1); } else { @@ -738,6 +759,8 @@ class uri { } } + bool operator==(const uri& rhs) const = default; + // get fragment // hi <3 @@ -778,12 +801,11 @@ class uri { return static_cast(t_port); } - std::string m_scheme; + type m_type = invalid; std::string m_host; std::string m_resource; uint16_t m_port; bool m_secure; - bool m_valid; bool m_ipv6_literal; }; diff --git a/websocketpp/version.hpp b/websocketpp/version.hpp index db263a1e9..e47a72ff5 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 = 8; +static int const minor_version = 9; /// Library patch version number -static int const patch_version = 3; +static int const patch_version = 0; /// Library pre-release flag /** * This is a textual flag indicating the type and number for pre-release @@ -54,7 +54,7 @@ static int const patch_version = 3; static char const prerelease_flag[] = "dev"; /// Default user agent string -static char const user_agent[] = "WebSocket++/0.8.3-dev"; +static char const user_agent[] = "WebSocket++/0.8.4-dev"; } // namespace websocketpp