Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abf927bb24 | |||
| e1f75756d1 | |||
| 322e3bafbf | |||
| 9019af0070 | |||
| e65b277017 | |||
| 8434139b67 | |||
| d4a4b79684 | |||
| 6b09310479 | |||
| 2b310fa80b | |||
| 0bb33e4bca | |||
| ebd0afd67d | |||
| 5f42305bbd | |||
| a2f182733a | |||
| 59fb9ece5d | |||
| 73c3b81411 | |||
| cc851aa167 | |||
| 0f2df92ae4 | |||
| 6b702cc050 | |||
| 92c1f0c415 | |||
| 9e5163364f | |||
| 8f0fab3e77 | |||
| 94fe989840 | |||
| 9da5ac93ec | |||
| 891a7a5ed5 | |||
| 1013aa89bc | |||
| 366679b50a | |||
| 94cc1ec5cb | |||
| 6f128a5499 | |||
| f5d14f3ad3 | |||
| 918745a7ea | |||
| cc7d74d8d0 | |||
| b154ac5907 | |||
| 0348510c8d | |||
| a495c8c529 | |||
| fabf8bf1ca | |||
| 3f913f97ef | |||
| ffaeba2ec7 | |||
| db304bfec4 | |||
| d3bec51d25 | |||
| 8b672f7372 | |||
| a4a79f6bf6 | |||
| 9dc013a3aa | |||
| 931ad40b5f | |||
| 842f745e24 | |||
| e6f8766707 | |||
| 17a2647454 | |||
| 323dec4efc | |||
| ee343fb282 | |||
| b76422aa7a | |||
| 563fcc6b45 | |||
| b6014cc79b | |||
| d917f0924b | |||
| e036a5aeab | |||
| ed335a37b5 | |||
| bb4cbf3d03 | |||
| a388817606 | |||
| b373f1ea6b | |||
| 28448c77a5 | |||
| ae097d6a93 | |||
| 02f0026a00 | |||
| aac65bdfd0 | |||
| 7b368e67ed | |||
| d0802994d8 | |||
| 20cc197967 | |||
| 89df55c577 | |||
| 9713455e94 | |||
| 044eda9cc4 | |||
| fc6ec59ab8 | |||
| a01cce3b1a | |||
| e1b39037f7 | |||
| f54fb0669b | |||
| 935c8a3264 | |||
| 1e12e7aff8 | |||
| bda1bd14cb | |||
| 5f52127cf7 | |||
| 999add0c4b | |||
| f7448bb1b0 | |||
| ed45dcc617 | |||
| b85affddf4 | |||
| 5500aef4ce | |||
| 4e2fb75d07 | |||
| 6a94162264 | |||
| 536db87c53 | |||
| 39c5f3ae3d | |||
| cd4af122c0 | |||
| b6817e4c7e | |||
| 72e55a76b5 | |||
| 93b75ccc7d | |||
| 3e8dbf767d | |||
| 3c77ce3cc7 | |||
| 54ce641d02 | |||
| e0bd3f7e95 | |||
| fb829e0895 | |||
| f9dbc0fd9d |
@@ -90,3 +90,5 @@ examples/wsperf/wsperf_client
|
||||
install
|
||||
Makefile
|
||||
bin
|
||||
|
||||
Testing/Temporary/CTestCostData.txt
|
||||
|
||||
+3
-5
@@ -2,18 +2,16 @@ language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
before_install:
|
||||
- sudo apt-get install libboost-chrono1.48-dev libboost-regex1.48-dev libboost-system1.48-dev libboost-thread1.48-dev libboost-test1.48-dev libboost-random1.48-dev -y
|
||||
#- sudo apt-get install libboost-chrono1.48-dev libboost-regex1.48-dev libboost-system1.48-dev libboost-thread1.48-dev libboost-test1.48-dev libboost-random1.48-dev -y
|
||||
- sudo add-apt-repository -y ppa:boost-latest/ppa && sudo apt-get update -q && sudo apt-get install -y libboost-chrono1.55-dev libboost-random1.55-dev libboost-regex1.55-dev libboost-system1.55-dev libboost-thread1.55-dev libboost-test1.55-dev
|
||||
env:
|
||||
global:
|
||||
- BOOST_INCLUDES=/usr/include
|
||||
- BOOST_LIBS=/usr/lib
|
||||
- BOOST_LIBS=/usr/lib/x86_64-linux-gnu
|
||||
script: scons -j 2 && scons test
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- permessage-deflate
|
||||
- experimental
|
||||
- 0.3.x-cmake
|
||||
- develop
|
||||
notifications:
|
||||
recipients:
|
||||
|
||||
+46
-23
@@ -1,18 +1,42 @@
|
||||
|
||||
############ Setup project and cmake
|
||||
|
||||
# Project name
|
||||
project (websocketpp)
|
||||
|
||||
# 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.6)
|
||||
cmake_minimum_required (VERSION 2.8.8)
|
||||
|
||||
############ 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)
|
||||
|
||||
# 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.
|
||||
# This must be set prior to any call to project or it will not be read correctly.
|
||||
# - Windows: Build the INSTALL project in your solution file.
|
||||
# - Linux/OSX: make install.
|
||||
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 7)
|
||||
set (WEBSOCKETPP_MINOR_VERSION 8)
|
||||
set (WEBSOCKETPP_PATCH_VERSION 0)
|
||||
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)
|
||||
|
||||
set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files")
|
||||
@@ -44,21 +68,6 @@ set (CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo;Debug" CACHE STRING "Conf
|
||||
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
include (CMakeHelpers)
|
||||
|
||||
############ 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)
|
||||
|
||||
# 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.
|
||||
# - Windows: Build the INSTALL project in your solution file.
|
||||
# - Linux/OSX: make install.
|
||||
if (MSVC)
|
||||
set (CMAKE_INSTALL_PREFIX "${WEBSOCKETPP_ROOT}/install")
|
||||
endif ()
|
||||
|
||||
############ Build customization
|
||||
|
||||
@@ -69,6 +78,8 @@ option (BUILD_TESTS "Build websocketpp tests." FALSE)
|
||||
|
||||
if (BUILD_TESTS OR BUILD_EXAMPLES)
|
||||
|
||||
enable_testing ()
|
||||
|
||||
############ Compiler specific setup
|
||||
|
||||
set (WEBSOCKETPP_PLATFORM_LIBS "")
|
||||
@@ -190,6 +201,10 @@ if (BUILD_TESTS OR BUILD_EXAMPLES)
|
||||
set (Boost_USE_STATIC_LIBS TRUE)
|
||||
endif ()
|
||||
|
||||
if (NOT Boost_USE_STATIC_LIBS)
|
||||
add_definitions (/DBOOST_TEST_DYN_LINK)
|
||||
endif ()
|
||||
|
||||
set (Boost_FIND_REQUIRED TRUE)
|
||||
set (Boost_FIND_QUIETLY TRUE)
|
||||
set (Boost_DEBUG FALSE)
|
||||
@@ -244,8 +259,16 @@ print_used_build_config()
|
||||
|
||||
export (PACKAGE websocketpp)
|
||||
|
||||
configure_file (websocketpp-config.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-config.cmake" @ONLY)
|
||||
configure_file (websocketpp-configVersion.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-configVersion.cmake" @ONLY)
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(websocketpp-config.cmake.in
|
||||
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-config.cmake"
|
||||
PATH_VARS INSTALL_INCLUDE_DIR
|
||||
INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
write_basic_package_version_file("${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-configVersion.cmake"
|
||||
VERSION ${WEBSOCKETPP_VERSION}
|
||||
COMPATIBILITY ExactVersion)
|
||||
|
||||
# Install the websocketpp-config.cmake and websocketpp-configVersion.cmake
|
||||
install (FILES
|
||||
|
||||
+16
-2
@@ -34,6 +34,11 @@ elif os.environ.has_key('BOOST_INCLUDES') and os.environ.has_key('BOOST_LIBS'):
|
||||
else:
|
||||
raise SCons.Errors.UserError, "Neither BOOST_ROOT, nor BOOST_INCLUDES + BOOST_LIBS was set!"
|
||||
|
||||
## Custom OpenSSL
|
||||
if os.environ.has_key('OPENSSL_PATH'):
|
||||
env.Append(CPPPATH = os.path.join(os.environ['OPENSSL_PATH'], 'include'))
|
||||
env.Append(LIBPATH = os.environ['OPENSSL_PATH'])
|
||||
|
||||
if os.environ.has_key('WSPP_ENABLE_CPP11'):
|
||||
env['WSPP_ENABLE_CPP11'] = True
|
||||
else:
|
||||
@@ -93,8 +98,7 @@ if env['PLATFORM'].startswith('win'):
|
||||
#env['LIBPATH'] = env['BOOST_LIBS']
|
||||
pass
|
||||
else:
|
||||
env['LIBPATH'] = ['/usr/lib',
|
||||
'/usr/local/lib'] #, env['BOOST_LIBS']
|
||||
env.Append(LIBPATH = ['/usr/lib', '/usr/local/lib'])
|
||||
|
||||
# Compiler specific warning flags
|
||||
if env['CXX'].startswith('g++'):
|
||||
@@ -227,10 +231,17 @@ if not env['PLATFORM'].startswith('win'):
|
||||
# echo_server
|
||||
echo_server = SConscript('#/examples/echo_server/SConscript',variant_dir = builddir + 'echo_server',duplicate = 0)
|
||||
|
||||
# echo_client
|
||||
echo_client = SConscript('#/examples/echo_client/SConscript',variant_dir = builddir + 'echo_client',duplicate = 0)
|
||||
|
||||
# print_client
|
||||
print_client = SConscript('#/examples/print_client/SConscript',variant_dir = builddir + 'print_client',duplicate = 0)
|
||||
|
||||
# echo_server_tls
|
||||
if tls_build:
|
||||
echo_server_tls = SConscript('#/examples/echo_server_tls/SConscript',variant_dir = builddir + 'echo_server_tls',duplicate = 0)
|
||||
echo_server_both = SConscript('#/examples/echo_server_both/SConscript',variant_dir = builddir + 'echo_server_both',duplicate = 0)
|
||||
print_client_tls = SConscript('#/examples/print_client_tls/SConscript',variant_dir = builddir + 'print_client_tls',duplicate = 0)
|
||||
|
||||
# broadcast_server
|
||||
broadcast_server = SConscript('#/examples/broadcast_server/SConscript',variant_dir = builddir + 'broadcast_server',duplicate = 0)
|
||||
@@ -260,6 +271,9 @@ subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',varia
|
||||
# telemetry_server
|
||||
telemetry_server = SConscript('#/examples/telemetry_server/SConscript',variant_dir = builddir + 'telemetry_server',duplicate = 0)
|
||||
|
||||
# external_io_service
|
||||
external_io_service = SConscript('#/examples/external_io_service/SConscript',variant_dir = builddir + 'external_io_service',duplicate = 0)
|
||||
|
||||
if not env['PLATFORM'].startswith('win'):
|
||||
# iostream_server
|
||||
iostream_server = SConscript('#/examples/iostream_server/SConscript',variant_dir = builddir + 'iostream_server',duplicate = 0)
|
||||
|
||||
+94
-10
@@ -1,15 +1,99 @@
|
||||
HEAD
|
||||
- Feature: Basic support for the permessage-deflate extension. #344
|
||||
- Improvement: Better automatic std::chrono feature detection for Visual Studio
|
||||
- Improvement: Major refactoring to bundled CMake build system. CMake can now be used to
|
||||
build all of the examples and the test suite. Thank you Thijs Wenker for a significant
|
||||
portion of this code. #378, #435, #449
|
||||
- Bug: Fix memory leak when init_asio produces an error. #454 Thank you Mark Grimes for
|
||||
reporting and fixing.
|
||||
- Bug: Fix crash when processing a specially crafted HTTP header. Thank you Eli Fidler for
|
||||
reporting, test cases, and a patch. #456
|
||||
- Examples: Add `print_client` example. This demonstrates a minimal non-TLS
|
||||
client that connects to a server and prints out the messages it receives.
|
||||
- Examples: Add `print_client_tls` example. This demonstrates a minimal TLS
|
||||
client, including basic support via Asio+OpenSSL for certificate chain
|
||||
and hostname verification.
|
||||
- Feature: Add getter for all headers to the HTTP parsers. This allows a
|
||||
wrapping library to enumerate all headers to send upstream. Thank you Jupp
|
||||
Müller for reporting and an initial pull request.
|
||||
- Improvement: Move the `socket_init_handler` to execute as a part of `init_asio`
|
||||
rather than connection `pre_init`. This allows setting of socket options prior
|
||||
to the bind/listen/accept system calls. Thank you ChristianRobl3D for
|
||||
reporting #530.
|
||||
- Compatibility: Make sure the chrono library used by Boost/Asio is in sync
|
||||
with what the websocketpp is using. Thank you Flow86 for reporting and a
|
||||
patch.
|
||||
- Compatibility: Update `telemetry_client` to use a slightly more cross platform
|
||||
method of sleeping. Should work on windows now. Thank you Meir Yanovich for
|
||||
reporting.
|
||||
- Bug: Store loggers in shared pointers to avoid crashes related to connections
|
||||
trying to write logs entries after their respective endpoint has been
|
||||
deallocated. Thank you Thalhammer for reporting and Jupp Müller for the
|
||||
patch. #539 #501
|
||||
- Bug: Change default listen backlog from 0 to `socket_base::max_connections`.
|
||||
#549. Thank you derwassi and zwelab for reporting and na1pir for providing
|
||||
access to hardware to debug the issue.
|
||||
- Bug: Fix a crash in the accept loop when `get_connection` fails. #551 Thank you
|
||||
Walter Gray for a patch.
|
||||
- Bug/Documentation: Fix incorrect example code that used
|
||||
`websocketpp::lib::error_code` instead of `websocketpp::exception`. Thank you
|
||||
heretic13 for reporting
|
||||
|
||||
0.6.0
|
||||
0.7.0 - 2016-02-22
|
||||
- MINOR BREAKING SOCKET POLICY CHANGE: Asio transport socket policy method
|
||||
`cancel_socket` will now return `lib::asio::error_code` instead of `void`.
|
||||
Custom Asio transport socket policies will need to be updated accordingly.
|
||||
This does not affect anyone using the bundled socket policies.
|
||||
- Feature: Basic support for the permessage-deflate extension. #344
|
||||
- Feature: Allow accessing the local endpoint when using the Asio transport.
|
||||
This allows inspection of the address and port in cases where they are chosen
|
||||
by the operating system rather than the user. Thank you Andreas Weis and
|
||||
Muzahid Hussain for reporting and related code. #458
|
||||
- Feature: Add support for subprotocols in Hybi00. Thank you Lukas Obermann
|
||||
for reporting and a patch. #518
|
||||
- Improvement: Better automatic std::chrono feature detection for Visual Studio
|
||||
- Improvement: Major refactoring to bundled CMake build system. CMake can now be
|
||||
used to build all of the examples and the test suite. Thank you Thijs Wenker
|
||||
for a significant portion of this code. #378, #435, #449
|
||||
- Improvement: In build environments where `lib::error_code` and
|
||||
`lib::asio::error_code` match (such as using `boost::asio` with
|
||||
`boost::system_error` or standalone asio with `std::system_error`, transport
|
||||
errors are passed through natively rather than being reported as a translated
|
||||
`pass_through` error type.
|
||||
- Improvement: Add a `get_transport_error` method to Asio transport connections
|
||||
to allow retrieving a machine readable native transport error.
|
||||
- Improvement: Add `connection::get_response`, `connection::get_response_code`,
|
||||
and `connection::get_response_msg` methods to allow accessing additional
|
||||
information about the HTTP responses that WebSocket++ sends. #465 Thank you
|
||||
Flow86 for reporting.
|
||||
- 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 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
|
||||
Grimes for reporting and fixing.
|
||||
- Bug: Fix crash when processing a specially crafted HTTP header. Thank you Eli
|
||||
Fidler for reporting, test cases, and a patch. #456
|
||||
- Bug: Fix an issue where standalone Asio builds that use TLS would not compile
|
||||
due to lingering boost code. #448 Thank you mjsp for reporting
|
||||
- Bug: Fix an issue where canceling a socket could throw an exception on some
|
||||
older Windows XP platforms. It now prints an appropriate set of log messages
|
||||
instead. Thank you Thijs Wenker for reporting and researching solutions. #460
|
||||
- Bug: Fix an issue where deferred HTTP connections that start sending a very
|
||||
long response before their HTTP handler ends would result in a second set of
|
||||
HTTP headers being injected into the output. Thank you Kevin Smith for
|
||||
reporting and providing test case details. #443
|
||||
- Bug: Fix an issue where the wrong type of strand was being created. Thank you
|
||||
Bastien Brunnenstein for reporting and a patch. #462
|
||||
- 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
|
||||
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.
|
||||
Fixes #490.
|
||||
- Bug: Fix a heap buffer overflow when checking very short URIs. Thank you
|
||||
Xavier Gibert for reporting and a patch #524
|
||||
- Compatibility: Fixes a number of build & config issues on Visual Studio 2015
|
||||
- Compatibility: Removes non-standards compliant masking behavior. #395, #469
|
||||
- Compatibility: Replace deprecated use of `auto_ptr` on systems where
|
||||
`unique_ptr` is available.
|
||||
|
||||
0.6.0 - 2015-06-02
|
||||
- MINOR BREAKING TRANSPORT POLICY CHANGE: Custom transport policies will now be
|
||||
required to include a new method `void set_uri(uri_ptr u)`. An implementation
|
||||
is not required. The stub transport policy includes an example stub method
|
||||
|
||||
@@ -14,10 +14,15 @@ macro (print_used_build_config)
|
||||
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 ("")
|
||||
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 ("")
|
||||
endmacro ()
|
||||
|
||||
# Adds the given folder_name into the source files of the current project.
|
||||
# Adds the given folder_name into the source files of the current project.
|
||||
# Use this macro when your module contains .cpp and .h files in several subdirectories.
|
||||
# Your sources variable needs to be WSPP_SOURCE_FILES and headers variable WSPP_HEADER_FILES.
|
||||
macro(add_source_folder folder_name)
|
||||
@@ -34,7 +39,7 @@ macro (init_target NAME)
|
||||
set (TARGET_NAME ${NAME})
|
||||
message ("** " ${TARGET_NAME})
|
||||
|
||||
# Include our own module path. This makes #include "x.h"
|
||||
# Include our own module path. This makes #include "x.h"
|
||||
# work in project subfolders to include the main directory headers.
|
||||
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endmacro ()
|
||||
@@ -49,15 +54,28 @@ macro (build_executable TARGET_NAME)
|
||||
|
||||
include_directories (${WEBSOCKETPP_ROOT} ${WEBSOCKETPP_INCLUDE})
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${WEBSOCKETPP_PLATFORM_LIBS})
|
||||
|
||||
set_target_properties (${TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${WEBSOCKETPP_BIN})
|
||||
set_target_properties (${TARGET_NAME} PROPERTIES DEBUG_POSTFIX d)
|
||||
endmacro ()
|
||||
|
||||
# Build executable and register as test
|
||||
macro (build_test TARGET_NAME)
|
||||
build_executable (${TARGET_NAME} ${ARGN})
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_LESS 3)
|
||||
message(WARNING "CMake too old to register ${TARGET_NAME} as a test")
|
||||
else ()
|
||||
add_test(NAME ${TARGET_NAME} COMMAND $<TARGET_FILE:${TARGET_NAME}>)
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
# Finalize target for all types
|
||||
macro (final_target)
|
||||
if ("${TARGET_LIB_TYPE}" STREQUAL "EXECUTABLE")
|
||||
install (TARGETS ${TARGET_NAME}
|
||||
RUNTIME DESTINATION "bin"
|
||||
install (TARGETS ${TARGET_NAME}
|
||||
RUNTIME DESTINATION "bin"
|
||||
CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES})
|
||||
endif ()
|
||||
|
||||
@@ -71,14 +89,17 @@ endmacro ()
|
||||
|
||||
macro (link_boost)
|
||||
target_link_libraries (${TARGET_NAME} ${Boost_LIBRARIES})
|
||||
set_property(TARGET ${TARGET_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR})
|
||||
endmacro ()
|
||||
|
||||
macro (link_openssl)
|
||||
target_link_libraries (${TARGET_NAME} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY})
|
||||
set_property(TARGET ${TARGET_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
|
||||
endmacro ()
|
||||
|
||||
macro (link_zlib)
|
||||
target_link_libraries (${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
set_property(TARGET ${TARGET_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${ZLIB_INCLUDE_DIR})
|
||||
endmacro ()
|
||||
|
||||
macro (include_subdirs PARENT)
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/** \page reference.config Config Reference
|
||||
|
||||
WebSocket++ uses a config template parameter to supply a number of compile type policy types and default numerical values for buffer sizes, timeouts, security behavior, etc. Swapping policies allows changing certain core library behavior designed to be pluggable.
|
||||
|
||||
A custom config can be made standalone or can subclass one of the bundled configs and just override a few things.
|
||||
|
||||
__Example__
|
||||
```
|
||||
// some config options may require additional includes or dependencies.
|
||||
// syslog logging policy, for example, requires <syslog.h>,
|
||||
// the permessage deflate settings require zlib.
|
||||
#include <websocketpp/logger/syslog.hpp>
|
||||
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
|
||||
|
||||
// Custom server config based on bundled asio config
|
||||
struct custom_server_config : public websocketpp::config::asio {
|
||||
// Replace default stream logger with a syslog logger
|
||||
typedef websocketpp::log::syslog<concurrency_type, websocketpp::log::elevel> elog_type;
|
||||
typedef websocketpp::log::syslog<concurrency_type, websocketpp::log::alevel> alog_type;
|
||||
|
||||
// Reduce read buffer size to optimize for small messages
|
||||
static const size_t connection_read_buffer_size = 1024;
|
||||
|
||||
// enable permessage_compress extension
|
||||
struct permessage_deflate_config {};
|
||||
|
||||
typedef websocketpp::extensions::permessage_deflate::enabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::server<custom_server_config> server_endpoint_type;
|
||||
```
|
||||
|
||||
Core Config Options
|
||||
-------------------
|
||||
|
||||
### Policies
|
||||
|
||||
Policies are classes used to allow clean swapping of behavior without changing the core library
|
||||
|
||||
| Typedef Name | Effect |
|
||||
| ------------------------- | -------------------------------------- |
|
||||
| concurrency_type | Concurrency policy |
|
||||
| elog_type | Error logger type |
|
||||
| alog_type | Access logger type |
|
||||
| request_type | HTTP request type |
|
||||
| response_type | HTTP response type |
|
||||
| message_type | Type to deliver recieved messages |
|
||||
| con_msg_manager_type | Connection level message manager |
|
||||
| endpoint_msg_manager_type | Endpoint level message manager |
|
||||
| rng_type | Random Number Generation policy |
|
||||
| transport_type | Transport policy to use |
|
||||
| endpoint_base | User overridable Endpoint base class |
|
||||
| connection_base | User overridable Connection base class |
|
||||
|
||||
### Timeouts Values
|
||||
|
||||
These represent the length of time (in ms) before the given operation is aborted
|
||||
|
||||
| Field | Type | Default | Operation |
|
||||
| ----------------------- | ---- | ------- | --------------------------- |
|
||||
| timeout_open_handshake | long | 5000 | Opening handshake |
|
||||
| timeout_close_handshake | long | 5000 | Closing handshake |
|
||||
| timeout_pong | long | 5000 | No pong recieved after ping |
|
||||
|
||||
### Performance tuning
|
||||
|
||||
| Field | Type | Default | Meaning |
|
||||
| --------------------------- | ------ | -------- | ------------------------------------------------------------------ |
|
||||
| connection_read_buffer_size | size_t | 16384 | Size of the per-connection read buffer |
|
||||
| enable_multithreading | bool | true | Disabling may reduce locking overhead for single threaded programs |
|
||||
|
||||
#### Connection Read Buffer
|
||||
|
||||
Each connection has an internal buffer of this size. A larger value will result in fewer trips through the library and less CPU overhead at the expense of increased memory usage per connection.
|
||||
|
||||
If your application primarily deals in very large messages you may want to try setting this value higher.
|
||||
|
||||
If your application has a lot of connections or primarily deals in small messages you may want to try setting this smaller.
|
||||
|
||||
### Security settings
|
||||
|
||||
| Field | Type | Default | Effect |
|
||||
| ---------------------- | ------ | ------- | -------------------------------------- |
|
||||
| drop_on_protocol_error | bool | false | Omit close handshake on protocol error |
|
||||
| silent_close | bool | false | Don't return close codes or reasons |
|
||||
| max_message_size | size_t | 32MB | WebSocket max message size limit |
|
||||
| max_http_body_size | size_t | 32MB | HTTP Parser's max body size limit |
|
||||
|
||||
#### Drop on protocol error
|
||||
Drop connections on protocol error rather than sending a close frame. Off by default. This may result in legitimate messages near the error being dropped as well. It may free up resources otherwise spent dealing with misbehaving clients.
|
||||
|
||||
#### Silent Close
|
||||
Silence close suppresses the return of detailed connection close information during the closing handshake. This information is useful for debugging and presenting useful errors to end users but may be undesirable for security reasons in some production environments. Close reasons could be used by an attacker to confirm that the endpoint is out of resources or be used to identify the WebSocket implementation in use.
|
||||
|
||||
Note: this will suppress *all* close codes, including those explicitly sent by local applications.
|
||||
|
||||
#### Max message size
|
||||
Default value for the processor's maximum message size. Maximum message size determines the point at which the library will drop a connection with the message_too_big protocol error.
|
||||
|
||||
#### Max HTTP header size
|
||||
Maximum body size determines the point at which the library will abort reading an HTTP message body and return the 413/request entity too large error.
|
||||
|
||||
Transport Config Options
|
||||
------------------------
|
||||
|
||||
### Policies
|
||||
|
||||
Policies are classes used to allow clean swapping of behavior without changing the core library
|
||||
|
||||
| Typedef Name | Effect |
|
||||
| ---------------- | ------------------ |
|
||||
| concurrency_type | Concurrency Policy |
|
||||
| elog_type | Error logger type |
|
||||
| alog_type | Access logger type |
|
||||
| request_type | HTTP request type |
|
||||
| response_type | HTTP response type |
|
||||
|
||||
### Timeouts Values
|
||||
|
||||
These represent the length of time (in ms) before the given operation is aborted
|
||||
|
||||
| Field | Type | Default | Operation |
|
||||
| ------------------------ | ---- | ------- | --------------------------------------------- |
|
||||
| timeout_socket_pre_init | long | 5000 | Transport dependent |
|
||||
| timeout_proxy | long | 5000 | Proxy handshake |
|
||||
| timeout_socket_post_init | long | 5000 | Transport dependent (commonly: TLS handshake) |
|
||||
| timeout_dns_resolve | long | 5000 | DNS resolution |
|
||||
| timeout_connect | long | 5000 | TCP Connect |
|
||||
| timeout_socket_shutdown | long | 5000 | Socket shutdown |
|
||||
|
||||
### Performance tuning
|
||||
|
||||
| Field | Type | Default | Meaning |
|
||||
| --------------------------- | ------ | -------- | ------------------------------------------------------------------ |
|
||||
| enable_multithreading | bool | true | Disabling may reduce locking overhead for single threaded programs |
|
||||
|
||||
*/
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/** \page faq FAQ
|
||||
|
||||
## General Library Usage
|
||||
|
||||
### Can a handler be changed after a connection is established? Can one be removed?
|
||||
Yes, but not globally.
|
||||
|
||||
Handlers assigned to endpoints will be automatically copied to the connections created by that endpoint. Changing a handler on an endpoint will only affect future connections.
|
||||
|
||||
Once a particular connection is created, it's handlers can be changed individually by calling the `set_*_handler` methods. Once changed, all future events of that type for that connection will use the new handler.
|
||||
|
||||
To remove a handler that was previously set, call the set method with `nullptr` or `NULL`.
|
||||
|
||||
### Can I reject or conditionally accept a connection
|
||||
Yes. The `validate` handler is called after the initial handshake has been recieved but before WebSocket++ has responded. This gives you the opportunity to inspect the incoming connection request, its headers, origin, subprotocols, and the remote endpoint IP. Return `true` from the validate handler to accept the connection and `false` to reject it.
|
||||
|
||||
To set a custom HTTP error message for your rejection, use `websocketpp::connection::set_status` and (optionally) `websocketpp::connection::set_body()` to set the HTTP status code and error message body text. If you do not set body text a message will be generated automatically based on the status code.
|
||||
|
||||
### How do I negotiate subprotocols?
|
||||
WebSocket connections may offer a particular subprotocol they want to use. The WebSocket protocol does not define the meaning or interpretation of the subprotocol. This interpretation is left up to the individual application endpoints.
|
||||
|
||||
WebSocket++ servers can read the requested subprotocols during the `validate` handler by calling `websocketpp::connection::get_requested_subprotocols`. The list is ordered by client priority. You may optionally choose one of these subprotocols with `websocketpp::connection::select_subprotocol`. The handshake will then complete and let the client know which one was chosen. If you do not choose any, the "blank"/empty/none subprotocol will be used.
|
||||
|
||||
WebSocket++ clients can add a subprotocol to an outgoing connection by calling `websocketpp::connection::add_subprotocol` before calling `websocketpp::client::connect`. The order of adding will be interpreted as the order of preference.
|
||||
|
||||
In both caases, after the connection has been established, the selected subprotocol is available via the `websocketpp::connection::get_subprotocol` method.
|
||||
|
||||
Note: some browsers will allow the connection to continue if they requested a subprotocol and your server doesn't select one. Others will reject the connection.
|
||||
|
||||
### 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:
|
||||
|
||||
- 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.
|
||||
|
||||
__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.
|
||||
|
||||
__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.
|
||||
|
||||
### Is there a way to check the validity of a `connection_hdl`?
|
||||
|
||||
Sometimes, not generally though, because there isn’t a way to check if a TCP connection is valid.
|
||||
|
||||
You can try upgrading your hdl to a full connection_ptr using `websocketpp::endpoint::get_con_from_hdl`. If this fails, the hdl is definitely invalid. If it succeeds it may or may not be. The only way to tell definitively is to try and send something (either a message or a ping).
|
||||
|
||||
If you handle errors from methods like send, ping, close, etc correctly then you shouldn’t have to worry about accidentally sending to dead connections. The send/ping/pong/close methods will set or throw a specific error in the case that you tried to send something but the connection was closed/gone/etc.
|
||||
|
||||
### How do I fix the "address is in use" error when trying to restart my server?
|
||||
|
||||
Normally, for security purposes, operating systems prevent programs from listening on sockets created by other programs. When your program crashes and restarts, the new instance is a different program from the perspective of the operating system. As such it can’t listen on the socket address/port that the previous program was using until after a timeout occurs to make sure the old program was done with it.
|
||||
|
||||
The the first step for handling this is to make sure that you provide a method (signal handler, admin websocket message, etc) to perform a clean server shutdown. There is a question elsewhere in this FAQ that describes the steps necessary for this.
|
||||
|
||||
The clean close strategy won't help in the case of crashes or other abnormal closures. An option to consider for these cases is the use of the SO_REUSEADDR socket option. This instructs the OS to not request an exclusive lock on the socket. This means that after your program crashes the replacement you start can immediately listen on that address/port combo again.
|
||||
|
||||
__Please note__: how this works exactly depends on your operating system. Additionally, not exclusively locking your listening socket could allow hijacking by other programs if you are running in a shared resource environment. For development this is generally no problem. For a production environment, think carefully about the security model. `websocketpp::transport::asio::endpoint::set_reuse_addr` is the method to do this. You must specify this setting before calling `websocketpp::transport::asio::endpoint::listen`.
|
||||
|
||||
### How do I send and recieve binary messages?
|
||||
|
||||
When supported by the remote endpoint, WebSocket++ allows reading and sending messages in the two formats specified in RFC6455, UTF8 text and binary. WebSocket++ performs UTF8 validation on all outgoing text messages to ensure that they meet the specification. Binary messages do not have any additional processing and their interpretation is left entirely to the library user.
|
||||
|
||||
To determine the type of an incoming message, use `websocketpp::message_buffer::message::get_opcode`. The relevant return values are `websocketpp::frame::opcode::text` and `websocketpp::frame::opcode::binary`. There is no difference in how payloads are retrieved between these modes, only in how WebSocket++ validated the contents and how the library user is to interpret the data.
|
||||
|
||||
To specify the type of an outgoing message, use the frame opcode values listed above as the second op parameter for `websocketpp::connection::send`. There are two relevant overloads of send. One that takes a `std::string` and defaults to op=text. The other that takes a `void const *` and a `size_t` length and defaults to op=binary. Note: You can send binary messages via the string overload and text messages via the void * overload. In the case that you are manually building a message buffer rather than using the automatic send member functions, you can pass the opcode in as a parameter to the message buffer constructor or user the `websocketpp::message_buffer::message::set_opcode` member function to set or re-set it later.
|
||||
|
||||
## Dependency Management
|
||||
|
||||
### Can WebSocket++ be used without Boost?
|
||||
Yes. WebSocket++ only uses Boost features as polyfills for C++11 language features and libraries. If you have a C++11 compiler and standard library you can use WebSocket++ without Boost. In most cases setting your build environment to use the C++11 (or later) language dialect is sufficient to enable this mode of use.
|
||||
|
||||
With less common compilers (and sometimes very recently release compilers) there may be specific issues with certain libraries that aren't automatically detected by the library. For these situations there are additional defines available to fine tune which C++11 libraries and features are used. TODO: more details about them.
|
||||
|
||||
For the iostream/raw transport the C++11 standard library is sufficient. For the Asio based transports, there is no C++11 library that provides the networking capabilaties that Asio does. As such even with a C++11 build system, you will need a standalone copy of Asio to use if Boost Asio is not available.
|
||||
|
||||
### Can WebSocket++ be used with standalone Asio
|
||||
Yes. The process is the same as used with standalone Asio itself. Define `ASIO_STANDALONE` before including Asio or WebSocket++ headers. You will need to download a copy of the Asio headers separately (http://www.think-async.com) and make sure they are in your build system's include path.
|
||||
|
||||
### Can WebSocket++ be used without TLS or OpenSSL?
|
||||
Yes. When using the iostream/raw transport, there are no TLS features and OpenSSL is not required. When using the Asio transport TLS features are optional. You only need OpenSSL if you want to use TLS. You can only make or recieve encrypted connections (https/wss) if you have enabled TLS features.
|
||||
|
||||
Whether an Asio endpoint uses TLS or not is determined by its config template parameter. The default bundled `websocketpp::config::asio` and `websocketpp::config::asio_client` configs do not support TLS, the `websocketpp::config::asio_tls` and `websocketpp::config::asio_tls_client` do.
|
||||
|
||||
The `<websocketpp/config/asio.hpp>` and `<websocketpp/config/asio_client.hpp>` headers will include both the TLS and non-TLS varients of their respective configs and require the presence of OpenSSL. The `<websocketpp/config/asio_no_tls.hpp>` and `<websocketpp/config/asio_no_tls_client.hpp>` headers will include only the non-TLS configs and do not require OpenSSL.
|
||||
|
||||
### Build issues with TLS on recent versions of OS X
|
||||
Mac OS X ships a severely outdated version of the OpenSSL library. To securely use TLS with WebSocket++ on OS X you will need to install a modern version of OpenSSL via homebrew or compiling from source.
|
||||
|
||||
## Compression
|
||||
|
||||
### How do I use permessage-deflate in version 0.6.0-permessagedeflate and 0.7.0?
|
||||
|
||||
These versions of the library require a custom config to use the permessage-deflate extension. Here is a minimal example of such a custom config. You can also integrate these lines into an existing custom config.
|
||||
|
||||
Note that in these versions there is no fine grained control over which connections are compressed or not. Clients will request compression with the default settings and use it if the server supports it. Servers will accept whatever parameters clients request.
|
||||
|
||||
Outgoing messages by default will be compressed if compression was auto-negotiated during the handshake. There is an option to force a specific message to be sent uncompressed even if compression was negotiated. This may be useful for sending data that you know to be compressed already (images, zip files, etc).
|
||||
|
||||
|
||||
__Server Example__
|
||||
```
|
||||
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
|
||||
|
||||
struct deflate_server_config : public websocketpp::config::asio {
|
||||
// ... additional custom config if you need it for other things
|
||||
|
||||
/// permessage_compress extension
|
||||
struct permessage_deflate_config {};
|
||||
|
||||
typedef websocketpp::extensions::permessage_deflate::enabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::server<deflate_server_config> server_endpoint_type;
|
||||
```
|
||||
|
||||
__Client Example__
|
||||
```
|
||||
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
|
||||
|
||||
struct deflate_client_config : public websocketpp::config::asio_client {
|
||||
// ... additional custom config if you need it for other things
|
||||
|
||||
/// permessage_compress extension
|
||||
struct permessage_deflate_config {};
|
||||
|
||||
typedef websocketpp::extensions::permessage_deflate::enabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
};
|
||||
|
||||
typedef websocketpp::client<deflate_client_config> client_endpoint_type;
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Is it possible to terminate a malicious connection quickly, without tying up resources performing clean close steps,
|
||||
|
||||
Yes. The library will automatically detect and terminate connections that violate the WebSocket protocol. In cases where the library believes the remote endpoint to be malicious or sufficiently broken to be unlikely to understand or process the closing handshake, it will be omited.
|
||||
|
||||
If your application detects conditions above the protocol level that you believe to be malicious, for example, if you recognize an IP from a known denial of service attack, you can close the connection with two different levels of urgency. Use the standard `websocketpp::endpoint::close` or `websocketpp::connection::close` methods with one of the following special close codes:
|
||||
- `websocketpp::close::status::omit_handshake`: Omits the closing handshake, but cleanly closes the TCP connection.
|
||||
- `websocketpp::close::status::force_tcp_drop`: Forcibly drop the TCP connection.
|
||||
|
||||
Please note that usage of these disconnect methods results in a violation of the WebSocket protocol and may have negative reprocusions for the remote endpoint with respect to network timeouts. Please use caution when using them.
|
||||
|
||||
## Build Issues
|
||||
|
||||
### Getting compile errors related to `std::chrono`, `boost::chrono`, `waitable_timer`, or `steady_clock`
|
||||
|
||||
Your build system may be confused about whether it is supposed to be using `boost::chrono` or `std::chrono`. Boost automatically detects this setup on some compilers but not others. Defining `BOOST_ASIO_HAS_STD_CHRONO` can help. See http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/overview/cpp2011/chrono.html for more details.
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,27 @@
|
||||
/** \page getting_started Getting Started
|
||||
|
||||
WebSocket++ code is available on github at https://github.com/zaphoyd/websocketpp
|
||||
The official project homepage lives at http://www.zaphoyd.com/websocketpp
|
||||
|
||||
The git repository is organized into several directories:
|
||||
|
||||
- **docs**: This documentation
|
||||
- **examples**: Example programs that demonstrate how to build basic versions of some commonly used patterns for WebSocket clients and servers.
|
||||
- **test**: Unit tests that confirm that the code you have works properly and help detect platform specific issues.
|
||||
- **tutorials**: Detailed walkthroughs of a select set of the example programs.
|
||||
- **websocketpp**: All of the library code and default configuration files.
|
||||
|
||||
WebSocket++ is a header only library. You can start using it by including the websocketpp source directory in your project's include path and including the appropriate WebSocket++ headers in your program. You may also need to include and/or link to appropriate Boost/system libraries. TODO: More information: Building a program with WebSocket++, Walkthroughs of the example programs
|
||||
|
||||
WebSocket++ includes cmake and scons scripts for building the examples and unit tests. Neither system is needed unless you want to build tests or examples in an automated fashion.
|
||||
|
||||
__Usage questions__ should be posted to the project mailing list at http://groups.google.com/group/websocketpp/ or the IRC channel (\#websocketpp on freenode).
|
||||
|
||||
__Bugs and issues__ should be posted to the project GitHub issues queue: https://github.com/zaphoyd/websocketpp/issues.
|
||||
|
||||
__Pull requests__ on GitHub are welcome. Please make them against the `develop` branch.
|
||||
|
||||
WebSocket++ is written and maintained by Peter Thorson. You can contact me via GitHub messaging, IRC, or via email at websocket@zaphoyd.com.
|
||||
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/** \page reference.handlers Handler Reference
|
||||
|
||||
Handlers allow WebSocket++ programs to receive notifications about events
|
||||
that happen in relation to their connections. Some handlers also behave as
|
||||
hooks that give the program a chance to modify state or adjust settings before
|
||||
the connection continues.
|
||||
|
||||
Handlers are registered by calling the appropriate `set_*_handler` method on either an
|
||||
endpoint or connection. The * refers to the name of the handler (as
|
||||
specified in the signature field below). For example, to set the open handler,
|
||||
call `set_open_handler(...)`.
|
||||
|
||||
Setting handlers on an endpoint will result in them being copied as the default
|
||||
handler to all new connections created by that endpoint. Changing an endpoint's
|
||||
handlers will not affect connections that are already in progress. This includes
|
||||
connections that are in the listening state. As such, it is important to set any
|
||||
endpoint handlers before you call `endpoint::start_accept` or else the handlers
|
||||
will not be attached to your first connection.
|
||||
|
||||
Setting handlers on a connection will result in the handler being changed for
|
||||
that connection only, starting at the next time that handler is called. This can
|
||||
be used to change the handler during a connection.
|
||||
|
||||
Connection Handlers
|
||||
-------------------
|
||||
|
||||
These handlers will be called at most once per connection in the order specified below.
|
||||
|
||||
### Socket Init Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| --------------------- | ----------------------------------------------------- | -------------------- |
|
||||
| Socket initialization | `socket_init(connection_hdl, asio::ip::tcp::socket&)` | 0.3.0 Asio Transport |
|
||||
|
||||
This hook is triggered after the socket has been initialized but before a connection is established.
|
||||
It allows setting arbitrary socket options before connections are sent/recieved.
|
||||
|
||||
### TCP Pre-init Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ----------------------------- | ------------------------------ | -------------------- |
|
||||
| TCP established, no data sent | `tcp_pre_init(connection_hdl)` | 0.3.0 Asio Transport |
|
||||
|
||||
This hook is triggered after the TCP connection is established, but before any pre-WebSocket-handshake
|
||||
operations have been run. Common pre-handshake operations include TLS handshakes and proxy connections.
|
||||
|
||||
### TCP Post-init Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ----------------------- | ------------------------------------------ | ----------------------------- |
|
||||
| Request for TLS context | `tls_context_ptr tls_init(connection_hdl)` | 0.3.0 Asio Transport with TLS |
|
||||
|
||||
This hook is triggered before the TLS handshake to request the TLS context to use. You must
|
||||
return a pointer to a configured TLS conext to continue. This provides the opportuinity to
|
||||
set up the TLS settings, certificates, etc.
|
||||
|
||||
### Validate Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ------------------------------------- | ------------------------------- | ---------------------------- |
|
||||
| Hook to accept or reject a connection | `bool validate(connection_hdl)` | 0.3.0 Core, Server role only |
|
||||
|
||||
This hook is triggered for servers during the opening handshake after the request has been
|
||||
processed but before the response has been sent. It gives a program the opportunity to inspect
|
||||
headers and other connection details and either accept or reject the connection. Validate happens
|
||||
before the open or fail handler.
|
||||
|
||||
Return true to accept the connection, false to reject. If no validate handler is registered,
|
||||
all connections will be accepted.
|
||||
|
||||
### Open Connection Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ------------------------- | ---------------------- | ------------ |
|
||||
| Successful new connection | `open(connection_hdl)` | 0.3.0 Core |
|
||||
|
||||
Either open or fail will be called for each connection. Never both. All
|
||||
connections that begin with an open handler call will also have a matching
|
||||
close handler call when the connection ends.
|
||||
|
||||
### Fail Connection Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ----------------------------------- | ---------------------- | ------------ |
|
||||
| Connection failed (before opening) | `fail(connection_hdl)` | 0.3.0 Core |
|
||||
|
||||
Either open or fail will be called for each connection. Never both. Connections
|
||||
that fail will never have a close handler called.
|
||||
|
||||
### Close Connection Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| --------------------------------- | ----------------------- | ------------ |
|
||||
| Connection closed (after opening) | `close(connection_hdl)` | 0.3.0 Core |
|
||||
|
||||
Close will be called exactly once for every connection that open was called for.
|
||||
Close is not called for failed connections.
|
||||
|
||||
Message Handlers
|
||||
----------------
|
||||
|
||||
These handers are called in response to incoming messages or message like events. They only will be called while the connection is in the open state.
|
||||
|
||||
### Message Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| --------------------- | -------------------------------------- | ------------ |
|
||||
| Data message recieved | `message(connection_hdl, message_ptr)` | 0.3.0 Core |
|
||||
|
||||
Applies to all non-control messages, including both text and binary opcodes. The
|
||||
`message_ptr` type and its API depends on your endpoint type and its config.
|
||||
|
||||
### Ping Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ------------- | ---------------------------------------- | ------------ |
|
||||
| Ping recieved | `bool ping(connection_hdl, std::string)` | 0.3.0 Core |
|
||||
|
||||
Second (string) argument is the binary ping payload. Handler return value
|
||||
indicates whether or not to respond to the ping with a pong. If no ping handler
|
||||
is set, WebSocket++ will respond with a pong containing the same binary data as
|
||||
the ping (Per requirements in RFC6455).
|
||||
|
||||
### Pong Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ------------- | ----------------------------------- | ------------ |
|
||||
| Pong recieved | `pong(connection_hdl, std::string)` | 0.3.0 Core |
|
||||
|
||||
Second (string) argument is the binary pong payload.
|
||||
|
||||
### Pong Timeout Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ---------------------------------- | ------------------------------------------- | ---------------------------------------- |
|
||||
| Timed out while waiting for a pong | `pong_timeout(connection_hdl, std::string)` | 0.3.0 Core, transport with timer support |
|
||||
|
||||
Triggered if there is no response to a ping after the configured duration. The second
|
||||
(string) argument is the binary payload of the unanswered ping.
|
||||
|
||||
### HTTP Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| --------------------- | --------------------- | ---------------------------- |
|
||||
| HTTP request recieved | `http(connection_hdl` | 0.3.0 Core, Server role only |
|
||||
|
||||
Called when HTTP requests that are not WebSocket handshake upgrade requests are
|
||||
recieved. Allows responding to regular HTTP requests. If no handler is registered
|
||||
a 426/Upgrade Required error is returned.
|
||||
|
||||
### Interrupt Handler
|
||||
|
||||
| Event | Signature | Availability |
|
||||
| ----------------------------------- | --------------------------- | ------------ |
|
||||
| Connection was manually interrupted | `interrupt(connection_hdl)` | 0.3.0 Core |
|
||||
|
||||
Interrupt events can be triggered by calling `endpoint::interrupt` or `connection::interrupt`.
|
||||
Interrupt is similar to a timer event with duration zero but with lower overhead. It is useful
|
||||
for single threaded programs to allow breaking up a very long handler into multiple parts and
|
||||
for multi threaded programs as a way for worker threads to signale to the main/network thread
|
||||
that an event is ready.
|
||||
|
||||
todo: write low and high watermark handlers
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,102 @@
|
||||
/** \page reference.logging Logging Reference
|
||||
|
||||
WebSocket++ has the capability of logging events during the lifetime of the connections that it processes. Each endpoint has two independent logging interfaces that are used by all connections created by that endpoint. The first is an access interface that allows logging routine events in the life of a connection (such as connect/disconnect and receipt of messages). The other is an error interface that allows logging non-routine problems or errors. Each interface has a number of different named channels that can be toggled on and off independently.
|
||||
|
||||
Exactly how these logs are processed and where they are written to depends on which logging policy is in use. Several logging policies are included by default and you can write your own policy if you need something more specialized. Selecting a policy is done via the \subpage reference.config "endpoint config".
|
||||
|
||||
Common functionality (all policies)
|
||||
-----------------------------------
|
||||
|
||||
### Logging Channels
|
||||
|
||||
Each logging interface is divided into 32 named channels. Log messages are written to a specific interface on a specific channel. Which log messages are actually printed is determined by which channels are enabled or not. Channels can be enabled or disabled either at compile time or at runtime.
|
||||
|
||||
### Enabling and Disabling Channels
|
||||
|
||||
Channels disabled at compile time are removed from the code entirely (assuming correct compiler optimization settings) and are not available for runtime enabling or disabling. To disable channels at compile time, use the `alog_level` and `elog_level` values within your \subpage reference.config "endpoint config". Channels not disabled at compile time can be enabled or disabled at runtime using the `websocketpp::endpoint::set_access_channels()`, `websocketpp::endpoint::clear_access_channels()`, `websocketpp::endpoint::set_error_channels()`, and `websocketpp::endpoint::clear_error_channels()` methods.
|
||||
|
||||
The set and clear functions act only on the channels specified. `set_access_channels(log::alevel::connect)` will enable logging of new connections. Following this with `set_access_channels(log::alevel::disconnect)` will enable logging of disconnections in addition to connections. Use `clear*` functions to disable a specific channel. Channels may be combined using bitwise operations to create aggregate packages of channels that may be set or cleared at once. Default packages include `websocketpp::log::alevel::all`, `websocketpp::log::elevel::all`, `websocketpp::log::alevel::none`, `websocketpp::log::elevel::none`. These represent all possible access/error channels and no access/error channels respectively. For convenience, setting none is aliased to clearing all.
|
||||
|
||||
### Examples
|
||||
|
||||
__Disable all__
|
||||
|
||||
`clear_access_channels(log::alevel::all)`
|
||||
|
||||
__Disable all (alternative method)__
|
||||
|
||||
`set_access_channels(log::alevel::none)`
|
||||
|
||||
__Multiple channels at once__
|
||||
|
||||
`log::alevel::message_payload | log::alevel::message_payload`
|
||||
|
||||
__All except one__
|
||||
|
||||
`log::alevel::all ^ log::alevel::message_payload`
|
||||
|
||||
__Default settings__
|
||||
|
||||
By default, only debug/development logging is disabled.
|
||||
|
||||
### Access to underlying loggers
|
||||
|
||||
Logging interfaces may be directly accessed via their associated endpoint or connection using get_alog() and get_elog(). This allows access to methods specific to the chosen logging policy.
|
||||
|
||||
Basic Logging (Default Policy)
|
||||
------------------------------
|
||||
|
||||
The basic logging policy (`websocketpp::log::basic`) writes logs to a std::ostream. By default, access logs are written to stdout and error logs are written to stderr. Each logging interface may be optionally redirected to an arbitrary C++ stream (including file streams) using the `websocketpp::log::basic::set_ostream()` method.
|
||||
|
||||
Syslog Logging
|
||||
--------------
|
||||
|
||||
The syslog logging policy (`websocketpp::log::syslog`) logs to POSIX syslog. It is included in the header `<websocketpp/logger/syslog.hpp>`. It requires a system with `<syslog.h>`.
|
||||
|
||||
Stub Logging
|
||||
------------
|
||||
|
||||
The stub logging policy (`websocketpp::log::stub`) implements the logging policy interface but ignores all input and provides no output. It can be used to stub out the logging system in tests or to completely disable and remove nearly all logging related code.
|
||||
|
||||
The stub logger also provides documentation for the minimal required interface to build a custom logging policy.
|
||||
|
||||
Log level reference
|
||||
-------------------
|
||||
|
||||
### Error Logging Levels
|
||||
|
||||
Each of these channels is in the namespace `websocketpp::log::elevel`
|
||||
|
||||
| Level | Description |
|
||||
| ------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| none | Special aggregate value representing "no levels" |
|
||||
| devel | Low level debugging information (warning: very chatty). Requires debug or custom config. |
|
||||
| library | Information about unusual system states or other minor internal library problems, less chatty than devel. |
|
||||
| info | Information about minor configuration problems or additional information about other warnings. |
|
||||
| warn | Information about important problems not severe enough to terminate connections. |
|
||||
| rerror | Recoverable error. Recovery may mean cleanly closing the connection with an appropriate error code to the remote endpoint. |
|
||||
| fatal | Unrecoverable error. This error will trigger immediate unclean termination of the connection or endpoint. |
|
||||
| all | Special aggregate value representing "all levels" |
|
||||
|
||||
### Access Logging Levels
|
||||
|
||||
Each of these channels is in the namespace `websocketpp::log::alevel`
|
||||
|
||||
| Level | Description |
|
||||
| --------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| none | Special aggregate value representing "no levels" |
|
||||
| connect | One line for each new connection that includes a host of information including: the remote address, websocket version, requested resource, http code, remote user agent |
|
||||
| disconnect | One line for each connection that is closed. Includes closing codes and reasons |
|
||||
| control | One line per control message |
|
||||
| frame_header | One line per frame, includes the full frame header |
|
||||
| frame_payload | One line per frame, includes the full message payload (warning: lots of output for large messages) |
|
||||
| message_header | Reserved |
|
||||
| message_payload | Reserved |
|
||||
| endpoint | Reserved |
|
||||
| debug_handshake | Extra information about opening handshakes |
|
||||
| debug_close | Extra information about closing handshakes |
|
||||
| devel | Development messages (warning: very chatty). Requires debug or custom config. |
|
||||
| app | Special channel for application specific logs. Not used by the library. |
|
||||
| all | Special aggregate value representing "all levels" |
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,22 @@
|
||||
.tabs, .tabs2, .tabs3, .navpath ul {
|
||||
background-image: none;
|
||||
background-color: #333;
|
||||
border: none;
|
||||
border-bottom: 1px solid #575757;
|
||||
}
|
||||
|
||||
.tablist li, .navpath li {
|
||||
background-image: none;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.tablist a, .navpath li.navelem a {
|
||||
color: #ccc;
|
||||
text-shadow: 0px 1px 1px black;
|
||||
}
|
||||
|
||||
.tablist a:hover, .navpath li.navelem a:hover {
|
||||
background-image: none;
|
||||
background-color: #444;
|
||||
color: #ccc;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/** \mainpage
|
||||
|
||||
WebSocket++ is a C++ library that can be used to implement WebSocket functionality. The goals of the project are to provide a WebSocket implementation that is portable, flexible, lightweight, low level, and high performance.
|
||||
|
||||
WebSocket++ does not intend to be used alone as a web application framework or full featured web services platform. As such the components, examples, and performance tuning are geared towards operation as a WebSocket client or server. There are some minimal convenience features that stray from this (for example the ability to respond to HTTP requests other than WebSocket Upgrades) but these are not the focus of the project. In particular WebSocket++ does not intend to implement any non-WebSocket related fallback options (ajax / long polling / comet / etc).
|
||||
|
||||
In order to remain compact and improve portability, the WebSocket++ project strives to reduce or eliminate external dependencies where possible and appropriate. WebSocket++ core has no dependencies other than the C++11 standard library. For non-C++11 compilers the Boost libraries provide drop in polyfills for the C++11 functionality used.
|
||||
|
||||
WebSocket++ implements a pluggable data transport component. The default component allows reduced functionality by using STL iostream or raw byte shuffling via reading and writing char buffers. This component has no non-STL dependencies and can be used in a C++11 environment without Boost. Also included is an Asio based transport component that provides full featured network client/server functionality. This component requires either Boost Asio or a C++11 compiler and standalone Asio. As an advanced option, WebSocket++ supports custom transport layers if you want to provide your own using another library.
|
||||
|
||||
In order to accommodate the wide variety of use cases WebSocket++ has collected, the library is built in a way that most of the major components are loosely coupled and can be swapped out and replaced. WebSocket++ will attempt to track the future development of the WebSocket protocol and any extensions as they are developed.
|
||||
|
||||
- \subpage getting_started "Getting Started"
|
||||
- \subpage faq "FAQ"
|
||||
- \subpage tutorials "Tutorials"
|
||||
- \subpage md_changelog "Change Log / Version History"
|
||||
- Reference
|
||||
- \subpage reference.handlers "Handler Reference"
|
||||
- \subpage reference.config "Config Reference"
|
||||
- \subpage reference.logging "Logging Reference"
|
||||
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/** \page tutorials Tutorials
|
||||
|
||||
These tutorials are works in progress, some are more complete than others.
|
||||
|
||||
- \subpage md_tutorials_utility_client_utility_client
|
||||
- \subpage md_tutorials_utility_server_utility_server
|
||||
- \subpage md_tutorials_broadcast_tutorial_broadcast_tutorial
|
||||
- \subpage md_tutorials_chat_tutorial_chat_tutorial
|
||||
|
||||
*/
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
connection_data data;
|
||||
|
||||
data.sessionid = m_next_sessionid++;
|
||||
data.name = "";
|
||||
data.name.clear();
|
||||
|
||||
m_connections[hdl] = data;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
void on_message(connection_hdl hdl, server::message_ptr msg) {
|
||||
connection_data& data = get_data_from_hdl(hdl);
|
||||
|
||||
if (data.name == "") {
|
||||
if (data.name.empty()) {
|
||||
data.name = msg->get_payload();
|
||||
std::cout << "Setting name of connection with sessionid "
|
||||
<< data.sessionid << " to " << data.name << std::endl;
|
||||
|
||||
@@ -19,6 +19,7 @@ using websocketpp::lib::bind;
|
||||
|
||||
using websocketpp::lib::thread;
|
||||
using websocketpp::lib::mutex;
|
||||
using websocketpp::lib::lock_guard;
|
||||
using websocketpp::lib::unique_lock;
|
||||
using websocketpp::lib::condition_variable;
|
||||
|
||||
@@ -71,27 +72,30 @@ public:
|
||||
}
|
||||
|
||||
void on_open(connection_hdl hdl) {
|
||||
unique_lock<mutex> lock(m_action_lock);
|
||||
//std::cout << "on_open" << std::endl;
|
||||
m_actions.push(action(SUBSCRIBE,hdl));
|
||||
lock.unlock();
|
||||
{
|
||||
lock_guard<mutex> guard(m_action_lock);
|
||||
//std::cout << "on_open" << std::endl;
|
||||
m_actions.push(action(SUBSCRIBE,hdl));
|
||||
}
|
||||
m_action_cond.notify_one();
|
||||
}
|
||||
|
||||
void on_close(connection_hdl hdl) {
|
||||
unique_lock<mutex> lock(m_action_lock);
|
||||
//std::cout << "on_close" << std::endl;
|
||||
m_actions.push(action(UNSUBSCRIBE,hdl));
|
||||
lock.unlock();
|
||||
{
|
||||
lock_guard<mutex> guard(m_action_lock);
|
||||
//std::cout << "on_close" << std::endl;
|
||||
m_actions.push(action(UNSUBSCRIBE,hdl));
|
||||
}
|
||||
m_action_cond.notify_one();
|
||||
}
|
||||
|
||||
void on_message(connection_hdl hdl, server::message_ptr msg) {
|
||||
// queue message up for sending by processing thread
|
||||
unique_lock<mutex> lock(m_action_lock);
|
||||
//std::cout << "on_message" << std::endl;
|
||||
m_actions.push(action(MESSAGE,hdl,msg));
|
||||
lock.unlock();
|
||||
{
|
||||
lock_guard<mutex> guard(m_action_lock);
|
||||
//std::cout << "on_message" << std::endl;
|
||||
m_actions.push(action(MESSAGE,hdl,msg));
|
||||
}
|
||||
m_action_cond.notify_one();
|
||||
}
|
||||
|
||||
@@ -109,13 +113,13 @@ public:
|
||||
lock.unlock();
|
||||
|
||||
if (a.type == SUBSCRIBE) {
|
||||
unique_lock<mutex> con_lock(m_connection_lock);
|
||||
lock_guard<mutex> guard(m_connection_lock);
|
||||
m_connections.insert(a.hdl);
|
||||
} else if (a.type == UNSUBSCRIBE) {
|
||||
unique_lock<mutex> con_lock(m_connection_lock);
|
||||
lock_guard<mutex> guard(m_connection_lock);
|
||||
m_connections.erase(a.hdl);
|
||||
} else if (a.type == MESSAGE) {
|
||||
unique_lock<mutex> con_lock(m_connection_lock);
|
||||
lock_guard<mutex> guard(m_connection_lock);
|
||||
|
||||
con_list::iterator it;
|
||||
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
|
||||
if (ec) {
|
||||
m_endpoint.get_alog().write(websocketpp::log::alevel::app,ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
//con->set_proxy("http://humupdates.uchicago.edu:8443");
|
||||
@@ -156,10 +157,10 @@ int main(int argc, char* argv[]) {
|
||||
try {
|
||||
perftest endpoint;
|
||||
endpoint.start(uri);
|
||||
} catch (const std::exception & e) {
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (std::exception const & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (websocketpp::lib::error_code e) {
|
||||
std::cout << e.message() << std::endl;
|
||||
} catch (...) {
|
||||
std::cout << "other exception" << std::endl;
|
||||
}
|
||||
|
||||
@@ -128,9 +128,9 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
std::cout << "Echo failed because: " << e
|
||||
<< "(" << e.message() << ")" << std::endl;
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << "Echo failed because: "
|
||||
<< "(" << e.what() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,10 +164,10 @@ int main() {
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
echo_server.run();
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (const std::exception & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (websocketpp::lib::error_code e) {
|
||||
std::cout << e.message() << std::endl;
|
||||
} catch (...) {
|
||||
std::cout << "other exception" << std::endl;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
file (GLOB SOURCE_FILES *.cpp)
|
||||
file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
init_target (echo_client)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "examples")
|
||||
@@ -0,0 +1,23 @@
|
||||
## echo_client example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is available build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + ['z']
|
||||
prgs += env_cpp11.Program('echo_client', ["echo_client.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system','random'],env) + [platform_libs] + [polyfill_libs] + ['z']
|
||||
prgs += env.Program('echo_client', ["echo_client.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
// pull out the type of messages sent by our config
|
||||
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) {
|
||||
std::cout << "on_message called with hdl: " << hdl.lock().get()
|
||||
<< " and message: " << msg->get_payload()
|
||||
<< std::endl;
|
||||
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
|
||||
if (ec) {
|
||||
std::cout << "Echo failed because: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Create a client endpoint
|
||||
client c;
|
||||
|
||||
std::string uri = "ws://localhost:9002";
|
||||
|
||||
if (argc == 2) {
|
||||
uri = argv[1];
|
||||
}
|
||||
|
||||
try {
|
||||
// Set logging to be pretty verbose (everything except message payloads)
|
||||
c.set_access_channels(websocketpp::log::alevel::all);
|
||||
c.clear_access_channels(websocketpp::log::alevel::frame_payload);
|
||||
|
||||
// Initialize ASIO
|
||||
c.init_asio();
|
||||
|
||||
// Register our message handler
|
||||
c.set_message_handler(bind(&on_message,&c,::_1,::_2));
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
client::connection_ptr con = c.get_connection(uri, ec);
|
||||
if (ec) {
|
||||
std::cout << "could not create connection because: " << ec.message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note that connect here only requests a connection. No network messages are
|
||||
// exchanged until the event loop starts running in the next line.
|
||||
c.connect(con);
|
||||
|
||||
// Start the ASIO io_service 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();
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,9 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
std::cout << "Echo failed because: " << e
|
||||
<< "(" << e.message() << ")" << std::endl;
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << "Echo failed because: "
|
||||
<< "(" << e.what() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ int main() {
|
||||
echo_server.set_access_channels(websocketpp::log::alevel::all);
|
||||
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
|
||||
|
||||
// Initialize ASIO
|
||||
// Initialize Asio
|
||||
echo_server.init_asio();
|
||||
|
||||
// Register our message handler
|
||||
|
||||
@@ -28,9 +28,9 @@ void on_message(EndpointType* s, websocketpp::connection_hdl hdl,
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
std::cout << "Echo failed because: " << e
|
||||
<< "(" << e.message() << ")" << std::endl;
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << "Echo failed because: "
|
||||
<< "(" << e.what() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* NOTES
|
||||
*
|
||||
* This example uses a number of standard classes through the websocketpp::lib
|
||||
* namespace. This is to allow easy switching between Boost, the C++11 STL, and
|
||||
* the standalone Asio library. Your program need not use these namespaces if
|
||||
* you do not need this sort of flexibility.
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/asio.hpp>
|
||||
|
||||
#include <websocketpp/server.hpp>
|
||||
@@ -12,7 +48,7 @@ using websocketpp::lib::bind;
|
||||
|
||||
// pull out the type of messages sent by our config
|
||||
typedef websocketpp::config::asio::message_type::ptr message_ptr;
|
||||
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
|
||||
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;
|
||||
|
||||
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
std::cout << "on_message called with hdl: " << hdl.lock().get()
|
||||
@@ -21,9 +57,9 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
std::cout << "Echo failed because: " << e
|
||||
<< "(" << e.message() << ")" << std::endl;
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << "Echo failed because: "
|
||||
<< "(" << e.what() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,27 +82,30 @@ enum tls_mode {
|
||||
};
|
||||
|
||||
context_ptr on_tls_init(tls_mode mode, websocketpp::connection_hdl hdl) {
|
||||
namespace asio = websocketpp::lib::asio;
|
||||
|
||||
std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl;
|
||||
std::cout << "using TLS mode: " << (mode == MOZILLA_MODERN ? "Mozilla Modern" : "Mozilla Intermediate") << std::endl;
|
||||
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
|
||||
|
||||
context_ptr ctx = websocketpp::lib::make_shared<asio::ssl::context>(asio::ssl::context::sslv23);
|
||||
|
||||
try {
|
||||
if (mode == MOZILLA_MODERN) {
|
||||
// Modern disables TLSv1
|
||||
ctx->set_options(boost::asio::ssl::context::default_workarounds |
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::no_sslv3 |
|
||||
boost::asio::ssl::context::no_tlsv1 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
ctx->set_options(asio::ssl::context::default_workarounds |
|
||||
asio::ssl::context::no_sslv2 |
|
||||
asio::ssl::context::no_sslv3 |
|
||||
asio::ssl::context::no_tlsv1 |
|
||||
asio::ssl::context::single_dh_use);
|
||||
} else {
|
||||
ctx->set_options(boost::asio::ssl::context::default_workarounds |
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::no_sslv3 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
ctx->set_options(asio::ssl::context::default_workarounds |
|
||||
asio::ssl::context::no_sslv2 |
|
||||
asio::ssl::context::no_sslv3 |
|
||||
asio::ssl::context::single_dh_use);
|
||||
}
|
||||
ctx->set_password_callback(bind(&get_password));
|
||||
ctx->use_certificate_chain_file("server.pem");
|
||||
ctx->use_private_key_file("server.pem", boost::asio::ssl::context::pem);
|
||||
ctx->use_private_key_file("server.pem", asio::ssl::context::pem);
|
||||
|
||||
// Example method of generating this file:
|
||||
// `openssl dhparam -out dh.pem 2048`
|
||||
|
||||
@@ -1,55 +1,57 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA77v5VbFaq+geVd/RCLXoKTWj/LgXGjiOXBpJZca3o5edi7N1
|
||||
Ej41/13jfZ4OxgFIRFAALd+fU4NsObXzskth3rJLmJjUtGajRIPqF2IUVjFMmybQ
|
||||
9dUDSqPzd/bG+ul0DP7DNuiBcLC8XNC3/kchPUzVq9gNDk56jXn87kMoIkVXBWQS
|
||||
Rwk0yCtELSfwxxRCrGKgQDfXnRdwmkPe42DmKtdGksXJlUhP1UVTAXaSw1Nz64LV
|
||||
9bJje/eoDsSe4OxifVVToE6ZxIW+r1jVgrcupeYXQrSHIztl2U/rx2JkGMN0eS+P
|
||||
qrsJmkmRBN1cpgmvSV7WoM3hj6Eq71z4Dfv6EwIDAQABAoIBAEXAL19bZsI1mv3p
|
||||
TOx34MB8tuXEuhQK+ICbtVdDZhLW/iOzZxCTwSo3qwTVg/7gSKJ3lFXEhprJ1idE
|
||||
ZU8u157vyUbw0JJceoxoxSdghgI9/cf2lz2vaHHDGgeBaYt/eSB+z1WeeGrNQUDQ
|
||||
CXiWQXmQbWq+Ra4v70BSieDY8UhmzCTRhvfZV80vaY/4SnHxJ9C8Nu8/An7U3pwq
|
||||
ccfTaWMp3Q5ztmEnExF6/b1SUsnI8rzFovw/4C50AU09N/Z6zZVFsXgJAT2Kd1Tx
|
||||
HfP1vUkWZ/TJ6kcmVIV9U3uMcedBD8Rb/3M0Qdp+eymQ+1oYQ3K3/a2RlUQIeXzf
|
||||
hRbE4wECgYEA/WyULrwBsvSMPqK0K9pZpv921Yl6d4bGnpxKze7ltc9o5/LsU35q
|
||||
8u29Es7cPyhyUDOsPW8v2CNjw54t+eFGsu59bBvXZNz5gB7hTUEyLntB24cO5PmN
|
||||
kVExcMDFC6NgPaauBtUhODievKJ2C8P7sRJDAVKE9KkavDddU7nSEjMCgYEA8ivG
|
||||
AqwNXYsiIFjAo4uj3kuBFt8LU/q4m6GXI+9qyxfiJjuF1dROx5qaQGiE85VaBFUu
|
||||
gpbnjZH7s+BBgoaQhrB4i6mztljdN1KKuAlnjhB+Fywg8m8N9lAi3wIaxnIEqDkU
|
||||
vFiIFPTBCk0U7IZ/OyCrKPQTE9ApWHfBwA/vWKECgYBQixrJg61SkBCms5Vpvprx
|
||||
zY2aLniC1o33yRqpOr09PG9OENH1c19QWCjYenBbjmJOhS2/1L+zBQRnHrXkDion
|
||||
Ik8wdeTORMDzbF0U7ZyiU0BKIjGkqn/I6LI68LlvinxC+9+hgkltkek5cLTt5lrv
|
||||
Gyu6ltx02e4KVdpOiuduKwKBgQC3U0PWigCkK8ttyUIrjG5Evcu/UKH2tPpDdpQ/
|
||||
8+JYVIAyiSTLtqjcmcDjuTvMWeeHGCTZXvtzRGvSw5VUBiIqlDTtJU6SX7s3QhkZ
|
||||
MKVf+kQ5roJShJeBOzDquWEjkPTNlEiKPErn8lCgR7HrS/XNAPIRUpOOkCp8ekwF
|
||||
5Qo/gQKBgAue3TeCeIzxh4PxSAk39Uaj+AaLvlYg8C+ucEodCNQZnJ7jOO0hHkfT
|
||||
NvZBes0VukCGlBLOGRQtC3kgtyrerscnDiZOhdnJrhPIdNPC0k/p/uAfh7+C5Sxm
|
||||
rCEL0S7kORdEjlIGd5j6ZEN/HZe8FyOYSjbFAoJSlAId9WMSxycQ
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDMYjHlTUeUGSys
|
||||
Fz5PZcvgS3gojBlEAOu2gXFQDcJ7kq6dJ4jKsPaH1Q5jAtEDkU/el8otzfacOgyp
|
||||
2ZxioRimpmcORWGU0bKJcenh4ZQ1oK1CQObjeYk1YgE7H8/sBetSdtL4n1rB8LIz
|
||||
AV/k6kwSZFu3/lSmc6g09H4efSKGKVBcVOjBamcvFGVH4KhM2NyL+ffeV5H2Ucxk
|
||||
ipyhpr4uxEoC3EV60sQxloqZb+upBM0LS4kVvaYMcn39XkUx3Z8FwN5+hFGwsWda
|
||||
tU8zDxEuRMrZxG7mwDXLBGehtQvoJIVMQbOuwBQcgAbsVyy1dxV3aczbLX0iGEuG
|
||||
eBhdFE+BAgMBAAECggEAQaPn0nUXYAEVz89HO8i9ybNzS9Jy6txA18SK1+MTawyY
|
||||
9/AShsZ+5vEORc5JwpOQyzSEwmE7qsEaABLbnvGOMTeQMY0m4dzXMj1bmCgSqYaJ
|
||||
HpYpkTUfU/2913dIF81u3nU7HI5RX6gmEyuF2MdG10FUE6ujFDJg+2DqgHA//kYD
|
||||
hkXFinVS2PuZs8d5xdzpF0aCIWTuOc+Fgsyhdm/lZRIzFdID45YUVuPIN2uh+GkM
|
||||
ENp/r1x7dPlDRqiL1ufP0mTQGs26S5kQSF8W0BClkOIOgmrhSON4+Vqhqx+ki/7w
|
||||
RY+7mmgdvt0uzYT+Lk2cDw4f89Rsh7rR1EieBpQ2YQKBgQDq6zAHWfweJmkugT0w
|
||||
HzI0UKfcOdzlJBwMu6tSgSHU99dnXlTwQY8sG7vtfRekokoo7XY4JsSk1n6E9OVy
|
||||
4UKuEvU1+llDGxtvHxEEGOAgwB8wxMuY4uNYgDVhTlUzr2ERcet7FOIGzxEWzSsg
|
||||
5vgnTQfyMzAh5/6k8CsHVI4u2wKBgQDeuYVCgg555lcc5rvTFxfU15d3fweSd78+
|
||||
akgIBaXAlFbxI+5znGPmKG/ii4N2XObC8B568fA2nIxw6M1xgbKyvvmN3ECYiqWv
|
||||
bx8x6Vg5Slg0vJr+DrPgvIKbOWEEKF/cfpTeeVLP0gUBT63mA3qezuRx1r0JJr7A
|
||||
k9a4Td9j0wKBgDmRQMfMaVgKGaRnz1LHkkn3qerx0wvj+Wu1YZpqQpwp0ANovm/R
|
||||
4P/yG+9qxCx4CKxW5K2F8pJibcavLLsmMGzwAF8l5lHnhqWIe2cBoYrlCb+tuibR
|
||||
Et1RLcOWqpJr2+GmhQo4Z9s7SvjHdlYtw4n9+oCDwrvMWj6ZDDJTqjQZAoGAEhRt
|
||||
RODZ2/texvHT/Wa6gISfvwuIydL+q0plXoFW2zMve5O3H5tqYJyXuIQqv8j60og7
|
||||
cS+CmGxM2j2Lr9MfdnMaPvHKLJfUq1ER7zNJ/hyS3HUS/9yhrXSgBYm63mOIpJWB
|
||||
8C1ZE5Ww4lJdg3Z01b9lu/f6kGucwHU/0OZBZBECgYAQ+dl2kKKd+lQ9O/LVz7oD
|
||||
goQMPYF+QZcEhY4vlYKkWVtR2A0CiY6XeTi6vO/qVUt/ht+UO3XIJFOjGV1VyORQ
|
||||
Bhibfstxl5s59jGlns5y5QqcRKzCiX74BKG0xQUtHgga7Od6L+GJKbJAPBfncYwW
|
||||
U7Tfwwi0WbbgQoy5Xr/5gg==
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEpzCCA4+gAwIBAgIJAMlCHvwnJ+F/MA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
|
||||
VQQGEwJVUzELMAkGA1UECBMCSUwxEDAOBgNVBAcTB0NoaWNhZ28xGTAXBgNVBAoT
|
||||
EFdlYlNvY2tldCsrIERlbW8xIjAgBgNVBAMTGXV0aWxpdGllcy53ZWJzb2NrZXRw
|
||||
cC5vcmcxJjAkBgkqhkiG9w0BCQEWF3dlYnNvY2tldHBwQHphcGhveWQuY29tMB4X
|
||||
DTE1MDQyNTE1NTk1M1oXDTE4MDMyODE1NTk1M1owgZMxCzAJBgNVBAYTAlVTMQsw
|
||||
CQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEZMBcGA1UEChMQV2ViU29ja2V0
|
||||
KysgRGVtbzEiMCAGA1UEAxMZdXRpbGl0aWVzLndlYnNvY2tldHBwLm9yZzEmMCQG
|
||||
CSqGSIb3DQEJARYXd2Vic29ja2V0cHBAemFwaG95ZC5jb20wggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDvu/lVsVqr6B5V39EItegpNaP8uBcaOI5cGkll
|
||||
xrejl52Ls3USPjX/XeN9ng7GAUhEUAAt359Tg2w5tfOyS2HeskuYmNS0ZqNEg+oX
|
||||
YhRWMUybJtD11QNKo/N39sb66XQM/sM26IFwsLxc0Lf+RyE9TNWr2A0OTnqNefzu
|
||||
QygiRVcFZBJHCTTIK0QtJ/DHFEKsYqBAN9edF3CaQ97jYOYq10aSxcmVSE/VRVMB
|
||||
dpLDU3PrgtX1smN796gOxJ7g7GJ9VVOgTpnEhb6vWNWCty6l5hdCtIcjO2XZT+vH
|
||||
YmQYw3R5L4+quwmaSZEE3VymCa9JXtagzeGPoSrvXPgN+/oTAgMBAAGjgfswgfgw
|
||||
HQYDVR0OBBYEFAWNBET93ZzSukXGkuGb93CfmUkDMIHIBgNVHSMEgcAwgb2AFAWN
|
||||
BET93ZzSukXGkuGb93CfmUkDoYGZpIGWMIGTMQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||
CBMCSUwxEDAOBgNVBAcTB0NoaWNhZ28xGTAXBgNVBAoTEFdlYlNvY2tldCsrIERl
|
||||
bW8xIjAgBgNVBAMTGXV0aWxpdGllcy53ZWJzb2NrZXRwcC5vcmcxJjAkBgkqhkiG
|
||||
9w0BCQEWF3dlYnNvY2tldHBwQHphcGhveWQuY29tggkAyUIe/Ccn4X8wDAYDVR0T
|
||||
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA2DRQSDmo2Q5t8MEjp6jddRIFts5t
|
||||
BDBWPSwJ0qvmheFt53MuLds2hhvMn42WGrKtVJiLiDyOWuUZ41REJZb1uaissUuY
|
||||
r9pLuP5QLdibx7+/30iDEY0OGTgtSTfgwNx8bIApBSHoZEN3/HaikqplBng2+6u/
|
||||
kTe6UnRrBJ+8JOGl+duhCXNPeSyLa2NcrxE9XpWC/k1kC9MTUF+2NuqCtK3zO8ci
|
||||
p0mqARWDSrEBYISh3dAOgDFrcX6zj+0MK+iswu3ijEdItGAjxjlQ2t4XT1T/CDbc
|
||||
ysHg04TJw7v682+v124GrnrAPYUK34OK8kFVJ60SNYRUi7euOCdTM4Lwkw==
|
||||
MIIFBTCCAu2gAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVT
|
||||
MQswCQYDVQQIDAJJTDEUMBIGA1UECgwLV2ViU29ja2V0KysxKjAoBgNVBAsMIVdl
|
||||
YlNvY2tldCsrIENlcnRpZmljYXRlIEF1dGhvcml0eTEkMCIGA1UEAwwbV2ViU29j
|
||||
a2V0KysgSW50ZXJtZWRpYXRlIENBMB4XDTE2MDYwODEyNDUxMloXDTI2MDYwNjEy
|
||||
NDUxMlowfjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGlj
|
||||
YWdvMRQwEgYDVQQKDAtXZWJTb2NrZXQrKzEgMB4GA1UECwwXV2ViU29ja2V0Kysg
|
||||
VExTIEV4YW1wbGUxGDAWBgNVBAMMD3dlYnNvY2tldHBwLm9yZzCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAMxiMeVNR5QZLKwXPk9ly+BLeCiMGUQA67aB
|
||||
cVANwnuSrp0niMqw9ofVDmMC0QORT96Xyi3N9pw6DKnZnGKhGKamZw5FYZTRsolx
|
||||
6eHhlDWgrUJA5uN5iTViATsfz+wF61J20vifWsHwsjMBX+TqTBJkW7f+VKZzqDT0
|
||||
fh59IoYpUFxU6MFqZy8UZUfgqEzY3Iv5995XkfZRzGSKnKGmvi7ESgLcRXrSxDGW
|
||||
iplv66kEzQtLiRW9pgxyff1eRTHdnwXA3n6EUbCxZ1q1TzMPES5EytnEbubANcsE
|
||||
Z6G1C+gkhUxBs67AFByABuxXLLV3FXdpzNstfSIYS4Z4GF0UT4ECAwEAAaOBhzCB
|
||||
hDALBgNVHQ8EBAMCBDAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwYAYDVR0RBFkwV4IP
|
||||
d2Vic29ja2V0cHAub3JnghN3d3cud2Vic29ja2V0cHAub3Jnghl1dGlsaXRpZXMu
|
||||
d2Vic29ja2V0cHAub3JnghRkb2NzLndlYnNvY2tldHBwLm9yZzANBgkqhkiG9w0B
|
||||
AQsFAAOCAgEAelJvIWFikBU3HVoP0icuoezTHGqABPLCeooTC/GELq7lHCFEjiqW
|
||||
p96Zc3vrk+0Z0tkYy3E0fpuzPtlTUhBzO3fMF41FpB5ix3W/tH9YJvrozlIuDD1I
|
||||
IEusxomeeiMRbyYpX/gkSOO74ylCzMEQVzleMNdpzpeXOg0Kp5z2JNShdEoT7eMR
|
||||
qkJQJjMdL6QeXUqWNvX1Zqb8v6VeWGWjuu/cl374P8D8bjn89VwZQ5HFqoLOhI5v
|
||||
XEYsMViZWwLSMcfWTU2Rdi0RxUZQVciLP/3GQROR1/0/e1J1kd7GsRWQMZcU20Vy
|
||||
jXBVAiWhW1bgd0XOrrFILsAmnBtinEJiE+h5UC4ksZtwWf9x1IhXGlpb9bmD4+Ud
|
||||
93wmqytPXBFL6wwlj4IYjjy0gU6xP6h7nwhHXnBlwFWGDpe8Cco9qgyJxJxBTtj9
|
||||
MbBv+BSLXJoniDASdk6RIqCjPWZtWbQ7j5mIKV0bdJQZpBX553QOy8AoIpJE32An
|
||||
FzR0SSCHOCgSAbqtM8CvLO6mquEJunmwKQx6xfos5N6ee+D+JtUFTw04TrjZUzFs
|
||||
Z7v3SN/N4Hd13iTBDSu4XY/tJYICvTRLYNrzQRh/XEVbEEVxXhL8rxNn5aL1pqrV
|
||||
yEnvHXrnSXWxTif1K+hS2HfTkQ6d1GjglvmwkoBqBHuRH0OJ1VguTqM=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
void on_message(connection_hdl hdl, server::message_ptr msg) {
|
||||
connection_ptr con = m_server.get_con_from_hdl(hdl);
|
||||
|
||||
if (con->name == "") {
|
||||
if (con->name.empty()) {
|
||||
con->name = msg->get_payload();
|
||||
std::cout << "Setting name of connection with sessionid "
|
||||
<< con->sessionid << " to " << con->name << std::endl;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
file (GLOB SOURCE_FILES *.cpp)
|
||||
file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
init_target (external_io_service)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "examples")
|
||||
@@ -0,0 +1,23 @@
|
||||
## Main development example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is available build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env_cpp11.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tcp_echo_server.hpp"
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> ws_echo_server;
|
||||
|
||||
// Define a callback to handle incoming messages
|
||||
void on_message(ws_echo_server* s, websocketpp::connection_hdl hdl, ws_echo_server::message_ptr msg) {
|
||||
std::cout << "on_message called with hdl: " << hdl.lock().get()
|
||||
<< " and message: " << msg->get_payload()
|
||||
<< std::endl;
|
||||
|
||||
// check for a special command to instruct the server to stop listening so
|
||||
// it can be cleanly exited.
|
||||
if (msg->get_payload() == "stop-listening") {
|
||||
s->stop_listening();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << "Echo failed because: "
|
||||
<< "(" << e.what() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
asio::io_service service;
|
||||
|
||||
// Add a TCP echo server on port 9003
|
||||
tcp_echo_server custom_http_server(service, 9003);
|
||||
|
||||
// Add a WebSocket echo server on port 9002
|
||||
ws_echo_server ws_server;
|
||||
ws_server.set_access_channels(websocketpp::log::alevel::all);
|
||||
ws_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
|
||||
|
||||
// The only difference in this code between an internal and external
|
||||
// io_service is the different constructor to init_asio
|
||||
ws_server.init_asio(&service);
|
||||
|
||||
// Register our message handler
|
||||
ws_server.set_message_handler(bind(&on_message,&ws_server,::_1,::_2));
|
||||
ws_server.listen(9002);
|
||||
ws_server.start_accept();
|
||||
|
||||
// TODO: add a timer?
|
||||
|
||||
// Start the Asio io_service run loop for all
|
||||
service.run();
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TCP Echo Server
|
||||
*
|
||||
* This file defines a simple TCP Echo Server. It is adapted from the Asio
|
||||
* example: cpp03/echo/async_tcp_echo_server.cpp
|
||||
*/
|
||||
|
||||
#include <websocketpp/common/asio.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
#include <websocketpp/common/functional.hpp>
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
namespace asio = websocketpp::lib::asio;
|
||||
|
||||
struct tcp_echo_session : websocketpp::lib::enable_shared_from_this<tcp_echo_session> {
|
||||
typedef websocketpp::lib::shared_ptr<tcp_echo_session> ptr;
|
||||
|
||||
tcp_echo_session(asio::io_service & service) : m_socket(service) {}
|
||||
|
||||
void start() {
|
||||
m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)),
|
||||
websocketpp::lib::bind(
|
||||
&tcp_echo_session::handle_read, shared_from_this(), _1, _2));
|
||||
}
|
||||
|
||||
void handle_read(const asio::error_code & ec, size_t transferred) {
|
||||
if (!ec) {
|
||||
asio::async_write(m_socket,
|
||||
asio::buffer(m_buffer, transferred),
|
||||
bind(&tcp_echo_session::handle_write, shared_from_this(), _1));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code & ec) {
|
||||
if (!ec) {
|
||||
m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)),
|
||||
bind(&tcp_echo_session::handle_read, shared_from_this(), _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket m_socket;
|
||||
char m_buffer[1024];
|
||||
};
|
||||
|
||||
struct tcp_echo_server {
|
||||
tcp_echo_server(asio::io_service & service, short port)
|
||||
: m_service(service)
|
||||
, m_acceptor(service, asio::ip::tcp::endpoint(asio::ip::tcp::v6(), port))
|
||||
{
|
||||
this->start_accept();
|
||||
}
|
||||
|
||||
void start_accept() {
|
||||
tcp_echo_session::ptr new_session(new tcp_echo_session(m_service));
|
||||
m_acceptor.async_accept(new_session->m_socket,
|
||||
bind(&tcp_echo_server::handle_accept, this, new_session, _1));
|
||||
}
|
||||
|
||||
void handle_accept(tcp_echo_session::ptr new_session, const asio::error_code & ec) {
|
||||
if (!ec) {
|
||||
new_session->start();
|
||||
}
|
||||
start_accept();
|
||||
}
|
||||
|
||||
asio::io_service & m_service;
|
||||
asio::ip::tcp::acceptor m_acceptor;
|
||||
};
|
||||
@@ -26,9 +26,9 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
} catch (websocketpp::exception const & e) {
|
||||
s->get_alog().write(websocketpp::log::alevel::app,
|
||||
"Echo Failed: "+e.message());
|
||||
std::string("Echo Failed: ")+e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
file (GLOB SOURCE_FILES *.cpp)
|
||||
file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
init_target (print_client)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "examples")
|
||||
@@ -0,0 +1,23 @@
|
||||
## Print client example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is available build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env_cpp11.Program('print_client', ["print_client.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
|
||||
prgs += env.Program('print_client', ["print_client.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
|
||||
std::cout << msg->get_payload() << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
client c;
|
||||
|
||||
std::string uri = "ws://localhost:9002";
|
||||
|
||||
if (argc == 2) {
|
||||
uri = argv[1];
|
||||
}
|
||||
|
||||
try {
|
||||
// Set logging to be pretty verbose (everything except message payloads)
|
||||
c.set_access_channels(websocketpp::log::alevel::all);
|
||||
c.clear_access_channels(websocketpp::log::alevel::frame_payload);
|
||||
c.set_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
// Initialize ASIO
|
||||
c.init_asio();
|
||||
|
||||
// Register our message handler
|
||||
c.set_message_handler(&on_message);
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
client::connection_ptr con = c.get_connection(uri, ec);
|
||||
if (ec) {
|
||||
std::cout << "could not create connection because: " << ec.message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note that connect here only requests a connection. No network messages are
|
||||
// exchanged until the event loop starts running in the next line.
|
||||
c.connect(con);
|
||||
|
||||
// Start the ASIO io_service 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();
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
file (GLOB SOURCE_FILES *.cpp)
|
||||
file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
if (OPENSSL_FOUND)
|
||||
|
||||
init_target (print_client_tls)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
link_openssl()
|
||||
final_target ()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "examples")
|
||||
|
||||
endif()
|
||||
@@ -0,0 +1,24 @@
|
||||
## Print client tls example
|
||||
##
|
||||
|
||||
Import('env')
|
||||
Import('env_cpp11')
|
||||
Import('boostlibs')
|
||||
Import('platform_libs')
|
||||
Import('polyfill_libs')
|
||||
Import('tls_libs')
|
||||
|
||||
env = env.Clone ()
|
||||
env_cpp11 = env_cpp11.Clone ()
|
||||
|
||||
prgs = []
|
||||
|
||||
# if a C++11 environment is available build using that, otherwise use boost
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env_cpp11.Program('print_client_tls', ["print_client_tls.cpp"], LIBS = ALL_LIBS)
|
||||
else:
|
||||
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
prgs += env.Program('print_client_tls', ["print_client_tls.cpp"], LIBS = ALL_LIBS)
|
||||
|
||||
Return('prgs')
|
||||
@@ -0,0 +1,66 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFxTCCA62gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtXZWJTb2Nr
|
||||
ZXQrKzEcMBoGA1UEAwwTV2ViU29ja2V0KysgUm9vdCBDQTAeFw0xNjA1MjUxMzU4
|
||||
MjdaFw0yNjA1MjMxMzU4MjdaMIGCMQswCQYDVQQGEwJVUzELMAkGA1UECAwCSUwx
|
||||
FDASBgNVBAoMC1dlYlNvY2tldCsrMSowKAYDVQQLDCFXZWJTb2NrZXQrKyBDZXJ0
|
||||
aWZpY2F0ZSBBdXRob3JpdHkxJDAiBgNVBAMMG1dlYlNvY2tldCsrIEludGVybWVk
|
||||
aWF0ZSBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMNaFAzlx0KJ
|
||||
gG15yRHI3xn9+B1woHG4uuOr124Sk1JllPcO3enusgIYTMl0FiYYW9CsyPoe4L0P
|
||||
wflbz20vDDjxmXG+NPgjuYmnPaq7q2JXYz+cShv9+o60EIwEIe+EWk1ZQs9YSdQ0
|
||||
r4UOxGVq6eEuWJi8Wh02cbnxdjwvrk7lTMFVY+z5EX8cCj6Tbrd0lyIf/0X8OkOb
|
||||
q2HOqqzTgT2apBCWCEW6grW6rtMOoDx93BOZDBEGz39sJ5i8AQ8XIdYCdUcOMdJU
|
||||
SCAw/MMyFTHXhv8hJdG5GcDSfc7woB9xRUf8UHuCH0nYkTb260TWvyDCYJy001ko
|
||||
SWoRbh2hVgPqQ9FTDMzMTY8T8C5u3BRfGN5PHuSPhwfHv/p1g4uPnltDBe4CNtOs
|
||||
wu8w1wbrr3uI7qETnqOzbXlcT7o4rCrrRQqLbNOssf2mMH+Phq6dINjXpZjiAhO0
|
||||
SURtBMmQdAZcQkGStzFitEkb2Py5LEIxQ068i8RCowTyD9+/jbO1fZyxJ4X8TDUe
|
||||
Xx48xWnu0i4f8/9ldnWLwX9h3ilaZVsr7buNYJoMlz+v73TQoWKSybJ2SMe/Cddj
|
||||
OZCy5r1UakuZhX6n1ScD/hbO8FEfmQRpAywYajyU4dZ9XMbf5bo6OAUqlJ2f4yYh
|
||||
VAy5mi1JHfD5PiJN90j79GXXvtBTJc4hAgMBAAGjZjBkMB0GA1UdDgQWBBTKMn5O
|
||||
3NUPpztL1bAz8UCsOBLpkjAfBgNVHSMEGDAWgBTNBBKZQN694xplMGyMruXFv27o
|
||||
eTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B
|
||||
AQsFAAOCAgEAyNkZUEAdP73th4/RLu0d2foMiIqKlcvw3JsW0tto3MT5lvQ5UugH
|
||||
OwluhWhnMLE0KsknQd7/p4ZwyZugWAYjGcDydp0GDIDfNBBEOQkOAL23KkYiRFqt
|
||||
VPBTZi9S7P2MJLY0j94liIg94nikhz/0q7JxxWFlvSHUjwZxXrbFjfZRPOS1vIq/
|
||||
/VK2QjUsdIXE3NOPYfQwd9FpG2YS8ZcMeipwNYVAs2FBEeWzGH1j6i2hP8FFBDYP
|
||||
0LTvJYOJvlCeyIvPBjKk9461/Z4CPJcKtKC59onQmiqSK/Juak/bpPoY7jJ228KG
|
||||
bEBzClIEHgbDiBewFTHbyOWhW2ySRLOGsPeqKDSbm4J1N5rfKnrSQB9PfOmWoRfJ
|
||||
vqPlXFSlpdgD4j/WnEumpvt78fT+cn+AkRG8tE5DQrCWZTK47TSWn902Fm0A19Rl
|
||||
pSbE9qsulXurOqEuOOayrzcUmbZ/jkU+wj+/tN4Gl8K98WbjcXvwz0sRL3SgRRrI
|
||||
awUdaGWKQHrTJNEOTisepUAuHVDmvuQz0j/Ru+PbB9K3GcKY6X6+o1c2JBC1V6KX
|
||||
aHHZQ+xPm+VEa1pG/QVHGpt2AbGUQlXwDYtOIRwEhO27tFbH8Q68s2cMLYjsF5gd
|
||||
MWuMYCPkFv10/V2f2lAIPSEzw2pldIGERcb4VG4xuD0qU+HH/aAID7k=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFpjCCA46gAwIBAgIJAL42eqbfw976MA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
|
||||
BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwL
|
||||
V2ViU29ja2V0KysxHDAaBgNVBAMME1dlYlNvY2tldCsrIFJvb3QgQ0EwHhcNMTYw
|
||||
NTI1MTM1MTUzWhcNMzYwNTIwMTM1MTUzWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||
CAwCSUwxEDAOBgNVBAcMB0NoaWNhZ28xFDASBgNVBAoMC1dlYlNvY2tldCsrMRww
|
||||
GgYDVQQDDBNXZWJTb2NrZXQrKyBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOC
|
||||
Ag8AMIICCgKCAgEA4QjJ0v5yri+pAN67I/XPz88D8oIczCW96CIuwc44aDC9Kptb
|
||||
9iY8xwbGCyQsFZ/1IQ74QfnXZSwq8EwedcBIdcyHdBu6qtNkCVKeDIZAMBef6Hx+
|
||||
tWSe1op3sDbUlT8NHiTxZCZWk/2/yIi8yPzQTi4y1vF04vvrQS5RFomCz17kdyOa
|
||||
NdxO5p+I4afdoVKtzA1aHoBqdTe7vzM3eww4AxKfgIEDdIuOGDiglI/b/frlwiOi
|
||||
SfTOsPzu52TOPW2d1Ad5BG4GuMpnTUOVnc8j18w9LdeXO0J10oVyCmwiPuzFCcDB
|
||||
g1xvVr5TXzIZ5J+qlso7+mUfZGH+nxOT7Tc78o1EvX6JbfQAI2PrpcksmJfFnN4l
|
||||
4XnXDW/eKl8AlLUr/cW5axAfql4QHJoBCZcfYldQpMoL5R1ikLtY53cOJpycFoWm
|
||||
1IEfkLBZ4C1old+KoaErG0+Aur8/kwAJGMnmMvZqGZ5pgXtVipOLy5TKuS6ZKO8g
|
||||
MRzalaF/naiu3pF+/sctaqkAPvOr65WrANNGxTQ93ePdyuT6sOEUKXxaXcTtAOOM
|
||||
5FCgX8dPxkOACxTrxppvb+bYmYL9GIuYDGYxSRu3Fm+04eXIh+uCqcuWPQuRPc5t
|
||||
VXvk/M0fPaJvKfP6lRAoE5Dp4qPRvL6tRVtOXfP6d+O+yGnxRoLKAW7ejoMCAwEA
|
||||
AaNjMGEwHQYDVR0OBBYEFM0EEplA3r3jGmUwbIyu5cW/buh5MB8GA1UdIwQYMBaA
|
||||
FM0EEplA3r3jGmUwbIyu5cW/buh5MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
|
||||
BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAQ4eY4LhW795wl9XuDVg2vOFTYmIS0
|
||||
OxrunFX4f3RjddbIbXzYmQ0cJ8pJ5l7eGYcg/DYQRY4Tk6LjXMs9VhIU10akqLS4
|
||||
qGE+Bmp3Jhu5NxZbKkY+k+kTAA1LYxFCjGjSV0v5QNLFULDAmGer2zWwU5DcDwwq
|
||||
8yWyBuI974UyE/49/TeckfqwBrb90LL2lFEwoL86XZK2IZMPyMBC/S1X5P/Kc15Q
|
||||
d8lwOPS5AirFkkrzs/px+mRia5U1uWKIPRLq9Medvjf8HR8SFWq9eRtkxiLaWyRv
|
||||
HBVyVRKCubCZR8psVLK/zrF+Bc+Hr9aAi3TuqTKjIOI7hrq5oJcJpebZDNoBIqoj
|
||||
kab13WcRwG+BQvuK1CEkd1aq8Nh2GX6Reb2Zv82/WntgP1a0sztbIGgrUBYQryb5
|
||||
HF79v4e2byY613SiQ3lz+g/AWxaZsYH80/Zl+hEwEtU4fFz34Jcv9Kvda1JpknBT
|
||||
Fi63ugfoNeNriO02AReMmDvuBG3X8RF1UQyBoTU3uZuW7X26MizEjiVCK9qaOLED
|
||||
WDSEoyKLe4JKd387CVlsCY8K/6fBlFTI/hJhggDz8pZFj3n2irUI44kjgOmoxOrW
|
||||
JY2jgY89AEMN9yOKkyQGara8pF9IJxTQ7jurYnWcUbompWeybJRwvWN0h+tGV+bd
|
||||
l/aq/5LwL3fVpg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <websocketpp/config/asio_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
|
||||
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
|
||||
std::cout << msg->get_payload() << std::endl;
|
||||
}
|
||||
|
||||
/// Verify that one of the subject alternative names matches the given hostname
|
||||
bool verify_subject_alternative_name(const char * hostname, X509 * cert) {
|
||||
STACK_OF(GENERAL_NAME) * san_names = NULL;
|
||||
|
||||
san_names = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
|
||||
if (san_names == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int san_names_count = sk_GENERAL_NAME_num(san_names);
|
||||
|
||||
bool result = false;
|
||||
|
||||
for (int i = 0; i < san_names_count; i++) {
|
||||
const GENERAL_NAME * current_name = sk_GENERAL_NAME_value(san_names, i);
|
||||
|
||||
if (current_name->type != GEN_DNS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char * dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName);
|
||||
|
||||
// Make sure there isn't an embedded NUL character in the DNS name
|
||||
if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
|
||||
break;
|
||||
}
|
||||
// Compare expected hostname with the CN
|
||||
result = (strcasecmp(hostname, dns_name) == 0);
|
||||
}
|
||||
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Verify that the certificate common name matches the given hostname
|
||||
bool verify_common_name(const char * hostname, X509 * cert) {
|
||||
// Find the position of the CN field in the Subject field of the certificate
|
||||
int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
|
||||
if (common_name_loc < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the CN field
|
||||
X509_NAME_ENTRY * common_name_entry = X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
|
||||
if (common_name_entry == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the CN field to a C string
|
||||
ASN1_STRING * common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
|
||||
if (common_name_asn1 == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char * common_name_str = (char *) ASN1_STRING_data(common_name_asn1);
|
||||
|
||||
// Make sure there isn't an embedded NUL character in the CN
|
||||
if (ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare expected hostname with the CN
|
||||
return (strcasecmp(hostname, common_name_str) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This code is derived from examples and documentation found ato00po
|
||||
* http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp03/ssl/client.cpp
|
||||
* and
|
||||
* https://github.com/iSECPartners/ssl-conservatory
|
||||
*/
|
||||
bool verify_certificate(const char * hostname, bool preverified, boost::asio::ssl::verify_context& ctx) {
|
||||
// The verify callback can be used to check whether the certificate that is
|
||||
// being presented is valid for the peer. For example, RFC 2818 describes
|
||||
// the steps involved in doing this for HTTPS. Consult the OpenSSL
|
||||
// documentation for more details. Note that the callback is called once
|
||||
// for each certificate in the certificate chain, starting from the root
|
||||
// certificate authority.
|
||||
|
||||
// Retrieve the depth of the current cert in the chain. 0 indicates the
|
||||
// actual server cert, upon which we will perform extra validation
|
||||
// (specifically, ensuring that the hostname matches. For other certs we
|
||||
// will use the 'preverified' flag from Asio, which incorporates a number of
|
||||
// non-implementation specific OpenSSL checking, such as the formatting of
|
||||
// certs and the trusted status based on the CA certs we imported earlier.
|
||||
int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle());
|
||||
|
||||
// if we are on the final cert and everything else checks out, ensure that
|
||||
// the hostname is present on the list of SANs or the common name (CN).
|
||||
if (depth == 0 && preverified) {
|
||||
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
|
||||
|
||||
if (verify_subject_alternative_name(hostname, cert)) {
|
||||
return true;
|
||||
} else if (verify_common_name(hostname, cert)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return preverified;
|
||||
}
|
||||
|
||||
/// TLS Initialization handler
|
||||
/**
|
||||
* WebSocket++ core and the Asio Transport do not handle TLS context creation
|
||||
* and setup. This callback is provided so that the end user can set up their
|
||||
* TLS context using whatever settings make sense for their application.
|
||||
*
|
||||
* As Asio and OpenSSL do not provide great documentation for the very common
|
||||
* case of connect and actually perform basic verification of server certs this
|
||||
* example includes a basic implementation (using Asio and OpenSSL) of the
|
||||
* following reasonable default settings and verification steps:
|
||||
*
|
||||
* - Disable SSLv2 and SSLv3
|
||||
* - Load trusted CA certificates and verify the server cert is trusted.
|
||||
* - Verify that the hostname matches either the common name or one of the
|
||||
* subject alternative names on the certificate.
|
||||
*
|
||||
* This is not meant to be an exhaustive reference implimentation of a perfect
|
||||
* TLS client, but rather a reasonable starting point for building a secure
|
||||
* TLS encrypted WebSocket client.
|
||||
*
|
||||
* If any TLS, Asio, or OpenSSL experts feel that these settings are poor
|
||||
* defaults or there are critically missing steps please open a GitHub issue
|
||||
* or drop a line on the project mailing list.
|
||||
*
|
||||
* Note the bundled CA cert ca-chain.cert.pem is the CA cert that signed the
|
||||
* cert bundled with echo_server_tls. You can use print_client_tls with this
|
||||
* CA cert to connect to echo_server_tls as long as you use /etc/hosts or
|
||||
* something equivilent to spoof one of the names on that cert
|
||||
* (websocketpp.org, for example).
|
||||
*/
|
||||
context_ptr on_tls_init(const char * hostname, websocketpp::connection_hdl) {
|
||||
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
|
||||
|
||||
try {
|
||||
ctx->set_options(boost::asio::ssl::context::default_workarounds |
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::no_sslv3 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
|
||||
ctx->set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
ctx->set_verify_callback(bind(&verify_certificate, hostname, ::_1, ::_2));
|
||||
|
||||
// Here we load the CA certificates of all CA's that this client trusts.
|
||||
ctx->load_verify_file("ca-chain.cert.pem");
|
||||
} catch (std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
client c;
|
||||
|
||||
std::string hostname = "localhost";
|
||||
std::string port = "9002";
|
||||
|
||||
|
||||
if (argc == 3) {
|
||||
hostname = argv[1];
|
||||
port = argv[2];
|
||||
} else {
|
||||
std::cout << "Usage: print_server_tls <hostname> <port>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string uri = "wss://" + hostname + ":" + port;
|
||||
|
||||
try {
|
||||
// Set logging to be pretty verbose (everything except message payloads)
|
||||
c.set_access_channels(websocketpp::log::alevel::all);
|
||||
c.clear_access_channels(websocketpp::log::alevel::frame_payload);
|
||||
c.set_error_channels(websocketpp::log::elevel::all);
|
||||
|
||||
// Initialize ASIO
|
||||
c.init_asio();
|
||||
|
||||
// Register our message handler
|
||||
c.set_message_handler(&on_message);
|
||||
c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1));
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
client::connection_ptr con = c.get_connection(uri, ec);
|
||||
if (ec) {
|
||||
std::cout << "could not create connection because: " << ec.message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note that connect here only requests a connection. No network messages are
|
||||
// exchanged until the event loop starts running in the next line.
|
||||
c.connect(con);
|
||||
|
||||
c.get_alog().write(websocketpp::log::alevel::app, "Connecting to " + uri);
|
||||
|
||||
// Start the ASIO io_service 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();
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -246,7 +246,7 @@ int main() {
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
std::string reason;
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
@@ -60,9 +60,9 @@ void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
*/
|
||||
try {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
} catch (const websocketpp::lib::error_code& e) {
|
||||
std::cout << "Echo failed because: " << e
|
||||
<< "(" << e.message() << ")" << std::endl;
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << "Echo failed because: "
|
||||
<< "(" << e.what() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,10 +96,10 @@ int main(int argc, char * argv[]) {
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
echo_server.run();
|
||||
} catch (websocketpp::exception const & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (const std::exception & e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
} catch (websocketpp::lib::error_code e) {
|
||||
std::cout << e.message() << std::endl;
|
||||
} catch (...) {
|
||||
std::cout << "other exception" << std::endl;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
// is configured.
|
||||
#include <websocketpp/common/thread.hpp>
|
||||
|
||||
/**
|
||||
* Define a semi-cross platform helper method that waits/sleeps for a bit.
|
||||
*/
|
||||
void wait_a_bit() {
|
||||
#ifdef WIN32
|
||||
Sleep(1000);
|
||||
#else
|
||||
sleep(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* The telemetry client connects to a WebSocket server and sends a message every
|
||||
* second containing an integer count. This example can be used as the basis for
|
||||
@@ -111,7 +122,7 @@ public:
|
||||
}
|
||||
|
||||
if (wait) {
|
||||
sleep(1);
|
||||
wait_a_bit();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -132,7 +143,7 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
wait_a_bit();
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
@@ -32,7 +32,6 @@ class telemetry_server {
|
||||
public:
|
||||
typedef websocketpp::connection_hdl connection_hdl;
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
typedef websocketpp::lib::lock_guard<websocketpp::lib::mutex> scoped_lock;
|
||||
|
||||
telemetry_server() : m_count(0) {
|
||||
// set up access channels to only log interesting things
|
||||
@@ -112,7 +111,7 @@ public:
|
||||
server::connection_ptr con = m_endpoint.get_con_from_hdl(hdl);
|
||||
|
||||
std::ifstream file;
|
||||
std::string filename = con->get_uri()->get_resource();
|
||||
std::string filename = con->get_resource();
|
||||
std::string response;
|
||||
|
||||
m_endpoint.get_alog().write(websocketpp::log::alevel::app,
|
||||
@@ -201,4 +200,4 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
s.run(docroot, port);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ int main() {
|
||||
|
||||
std::string cmd;
|
||||
int id;
|
||||
std::string message = "";
|
||||
std::string message;
|
||||
|
||||
ss >> cmd >> id;
|
||||
std::getline(ss,message);
|
||||
@@ -301,7 +301,7 @@ int main() {
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
std::string reason;
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
WebSocket++ (0.7.x-dev)
|
||||
WebSocket++ (0.8.0-dev)
|
||||
==========================
|
||||
|
||||
WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket
|
||||
@@ -28,7 +28,7 @@ Get Involved
|
||||
http://www.zaphoyd.com/websocketpp/
|
||||
|
||||
**User Manual**
|
||||
http://www.zaphoyd.com/websocketpp/manual/
|
||||
http://docs.websocketpp.org/
|
||||
|
||||
**GitHub Repository**
|
||||
https://github.com/zaphoyd/websocketpp/
|
||||
|
||||
@@ -4,7 +4,7 @@ file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
init_target (test_connection)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
build_test (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
|
||||
@@ -132,11 +132,14 @@ struct debug_config_client : public websocketpp::config::core {
|
||||
};
|
||||
|
||||
struct connection_setup {
|
||||
connection_setup(bool p_is_server) : c(p_is_server, "", alog, elog, rng) {}
|
||||
connection_setup(bool p_is_server)
|
||||
: alog(websocketpp::lib::make_shared<stub_config::alog_type>())
|
||||
, elog(websocketpp::lib::make_shared<stub_config::elog_type>())
|
||||
, c(p_is_server, "", alog, elog, rng) {}
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
stub_config::alog_type alog;
|
||||
stub_config::elog_type elog;
|
||||
websocketpp::lib::shared_ptr<stub_config::alog_type> alog;
|
||||
websocketpp::lib::shared_ptr<stub_config::elog_type> elog;
|
||||
stub_config::rng_type rng;
|
||||
websocketpp::connection<stub_config> c;
|
||||
};
|
||||
@@ -159,12 +162,17 @@ bool validate_set_ua(server* s, websocketpp::connection_hdl hdl) {
|
||||
}
|
||||
|
||||
void http_func(server* s, websocketpp::connection_hdl hdl) {
|
||||
using namespace websocketpp::http;
|
||||
|
||||
server::connection_ptr con = s->get_con_from_hdl(hdl);
|
||||
|
||||
std::string res = con->get_resource();
|
||||
|
||||
con->set_body(res);
|
||||
con->set_status(websocketpp::http::status_code::ok);
|
||||
con->set_status(status_code::ok);
|
||||
|
||||
BOOST_CHECK_EQUAL(con->get_response_code(), status_code::ok);
|
||||
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) {
|
||||
@@ -376,31 +384,31 @@ BOOST_AUTO_TEST_CASE( websocket_fail_unsupported_version ) {
|
||||
BOOST_CHECK(called);
|
||||
}
|
||||
|
||||
/*BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri ) {
|
||||
std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
// BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri ) {
|
||||
// std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
s.set_open_handler(bind(&on_open_print,&s,::_1));
|
||||
// server s;
|
||||
// websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
|
||||
// bool called = false;
|
||||
// s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
// s.set_open_handler(bind(&on_open_print,&s,::_1));
|
||||
|
||||
std::cout << run_server_test(s,input,true) << std::endl;
|
||||
BOOST_CHECK(called);
|
||||
}
|
||||
// std::cout << run_server_test(s,input,true) << std::endl;
|
||||
// BOOST_CHECK(called);
|
||||
// }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri_http ) {
|
||||
std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
// BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri_http ) {
|
||||
// std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
s.set_open_handler(bind(&on_open_print,&s,::_1));
|
||||
// server s;
|
||||
// websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
|
||||
// bool called = false;
|
||||
// s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
// s.set_open_handler(bind(&on_open_print,&s,::_1));
|
||||
|
||||
std::cout << run_server_test(s,input,true) << std::endl;
|
||||
BOOST_CHECK(called);
|
||||
}*/
|
||||
// std::cout << run_server_test(s,input,true) << std::endl;
|
||||
// BOOST_CHECK(called);
|
||||
// }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( websocket_fail_upgrade_required ) {
|
||||
std::string input = "GET /foo/bar HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
@@ -418,26 +426,26 @@ BOOST_AUTO_TEST_CASE( websocket_fail_upgrade_required ) {
|
||||
// TODO: set max message size mid connection test case
|
||||
// TODO: [maybe] set max message size in open handler
|
||||
|
||||
/*
|
||||
|
||||
BOOST_AUTO_TEST_CASE( user_reject_origin ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example2.com\r\n\r\n";
|
||||
std::string output = "HTTP/1.1 403 Forbidden\r\nServer: "+websocketpp::USER_AGENT+"\r\n\r\n";
|
||||
|
||||
BOOST_CHECK(run_server_test(input) == output);
|
||||
}
|
||||
// BOOST_AUTO_TEST_CASE( user_reject_origin ) {
|
||||
// std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example2.com\r\n\r\n";
|
||||
// std::string output = "HTTP/1.1 403 Forbidden\r\nServer: "+websocketpp::USER_AGENT+"\r\n\r\n";
|
||||
|
||||
BOOST_AUTO_TEST_CASE( basic_text_message ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
// BOOST_CHECK(run_server_test(input) == output);
|
||||
// }
|
||||
|
||||
unsigned char frames[8] = {0x82,0x82,0xFF,0xFF,0xFF,0xFF,0xD5,0xD5};
|
||||
input.append(reinterpret_cast<char*>(frames),8);
|
||||
// BOOST_AUTO_TEST_CASE( basic_text_message ) {
|
||||
// std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: "+websocketpp::USER_AGENT+"\r\nUpgrade: websocket\r\n\r\n**";
|
||||
// unsigned char frames[8] = {0x82,0x82,0xFF,0xFF,0xFF,0xFF,0xD5,0xD5};
|
||||
// input.append(reinterpret_cast<char*>(frames),8);
|
||||
|
||||
// std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: "+websocketpp::USER_AGENT+"\r\nUpgrade: websocket\r\n\r\n**";
|
||||
|
||||
// BOOST_CHECK( run_server_test(input) == output);
|
||||
// }
|
||||
|
||||
BOOST_CHECK( run_server_test(input) == output);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ if (OPENSSL_FOUND)
|
||||
|
||||
init_target (test_endpoint)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
build_test (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
link_openssl ()
|
||||
|
||||
@@ -13,7 +13,7 @@ if ( ZLIB_FOUND )
|
||||
file (GLOB SOURCE permessage_deflate.cpp)
|
||||
|
||||
init_target (test_permessage_deflate)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
link_zlib()
|
||||
final_target ()
|
||||
|
||||
@@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE( negotiate_server_no_context_takeover_invalid ) {
|
||||
|
||||
BOOST_AUTO_TEST_CASE( negotiate_server_no_context_takeover ) {
|
||||
ext_vars v;
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE( negotiate_client_no_context_takeover_invalid ) {
|
||||
|
||||
BOOST_AUTO_TEST_CASE( negotiate_client_no_context_takeover ) {
|
||||
ext_vars v;
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
@@ -282,7 +282,7 @@ BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_invalid ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_valid ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["client_max_window_bits"] = "";
|
||||
v.attr["client_max_window_bits"].clear();
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
|
||||
@@ -364,8 +364,8 @@ BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_smallest ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated1 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
@@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated1 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated2 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
v.attr["server_max_window_bits"] = "10";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
@@ -388,7 +388,7 @@ BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated2 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated3 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
v.attr["client_max_window_bits"] = "10";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
@@ -400,7 +400,7 @@ BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated3 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated4 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
v.attr["server_max_window_bits"] = "10";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
@@ -412,7 +412,7 @@ BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated4 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated5 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
v.attr["client_max_window_bits"] = "10";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
@@ -436,8 +436,8 @@ BOOST_AUTO_TEST_CASE( negotiate_two_client_initiated6 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated1 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
v.attr["server_max_window_bits"] = "10";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
@@ -449,8 +449,8 @@ BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated1 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated2 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
v.attr["client_max_window_bits"] = "10";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
@@ -462,7 +462,7 @@ BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated2 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated3 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
v.attr["server_max_window_bits"] = "10";
|
||||
v.attr["client_max_window_bits"] = "10";
|
||||
|
||||
@@ -475,7 +475,7 @@ BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated3 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated4 ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
v.attr["server_max_window_bits"] = "10";
|
||||
v.attr["client_max_window_bits"] = "10";
|
||||
|
||||
@@ -488,8 +488,8 @@ BOOST_AUTO_TEST_CASE( negotiate_three_client_initiated4 ) {
|
||||
BOOST_AUTO_TEST_CASE( negotiate_four_client_initiated ) {
|
||||
ext_vars v;
|
||||
|
||||
v.attr["server_no_context_takeover"] = "";
|
||||
v.attr["client_no_context_takeover"] = "";
|
||||
v.attr["server_no_context_takeover"].clear();
|
||||
v.attr["client_no_context_takeover"].clear();
|
||||
v.attr["server_max_window_bits"] = "10";
|
||||
v.attr["client_max_window_bits"] = "10";
|
||||
|
||||
@@ -569,7 +569,7 @@ BOOST_AUTO_TEST_CASE( compress_data_no_context_takeover ) {
|
||||
|
||||
websocketpp::http::attribute_list alist;
|
||||
|
||||
alist["server_no_context_takeover"] = "";
|
||||
alist["server_no_context_takeover"].clear();
|
||||
v.exts.enable_server_no_context_takeover();
|
||||
|
||||
v.exts.negotiate(alist);
|
||||
@@ -586,7 +586,7 @@ BOOST_AUTO_TEST_CASE( compress_data_no_context_takeover ) {
|
||||
BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( compress_in, decompress_out );
|
||||
|
||||
decompress_out = "";
|
||||
decompress_out.clear();
|
||||
|
||||
v.ec = v.exts.compress(compress_in,compress_out2);
|
||||
BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
|
||||
@@ -605,7 +605,7 @@ BOOST_AUTO_TEST_CASE( compress_data_no_context_takeover ) {
|
||||
BOOST_AUTO_TEST_CASE( compress_empty ) {
|
||||
ext_vars v;
|
||||
|
||||
std::string compress_in = "";
|
||||
std::string compress_in;
|
||||
std::string compress_out;
|
||||
std::string decompress_out;
|
||||
|
||||
@@ -616,8 +616,8 @@ BOOST_AUTO_TEST_CASE( compress_empty ) {
|
||||
|
||||
v.ec = v.exts.decompress(reinterpret_cast<const uint8_t *>(compress_out.data()),compress_out.size(),decompress_out);
|
||||
|
||||
compress_out = "";
|
||||
decompress_out = "";
|
||||
compress_out.clear();
|
||||
decompress_out.clear();
|
||||
|
||||
v.ec = v.exts.compress(compress_in,compress_out);
|
||||
BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
|
||||
@@ -637,7 +637,7 @@ BOOST_AUTO_TEST_CASE( decompress_data ) {
|
||||
ext_vars v;
|
||||
|
||||
uint8_t in[11] = {0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x00, 0x00, 0xff, 0xff};
|
||||
std::string out = "";
|
||||
std::string out;
|
||||
std::string reference = "Hello";
|
||||
|
||||
v.exts.init(true);
|
||||
|
||||
@@ -3,7 +3,7 @@ file (GLOB SOURCE_FILES parser.cpp)
|
||||
|
||||
init_target (test_http)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES})
|
||||
build_test (${TARGET_NAME} ${SOURCE_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
|
||||
+19
-7
@@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE( extract_token ) {
|
||||
BOOST_CHECK( ret.second == d1.begin()+3 );
|
||||
|
||||
ret = websocketpp::http::parser::extract_token(d2.begin(),d2.end());
|
||||
BOOST_CHECK( ret.first == "" );
|
||||
BOOST_CHECK( ret.first.empty() );
|
||||
BOOST_CHECK( ret.second == d2.begin()+0 );
|
||||
|
||||
ret = websocketpp::http::parser::extract_token(d2.begin()+1,d2.end());
|
||||
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE( extract_quoted_string ) {
|
||||
std::string d1 = "\"foo\"";
|
||||
std::string d2 = "\"foo\\\"bar\\\"baz\"";
|
||||
std::string d3 = "\"foo\" ";
|
||||
std::string d4 = "";
|
||||
std::string d4;
|
||||
std::string d5 = "foo";
|
||||
|
||||
std::pair<std::string,std::string::const_iterator> ret;
|
||||
@@ -147,11 +147,11 @@ BOOST_AUTO_TEST_CASE( extract_quoted_string ) {
|
||||
BOOST_CHECK( ret.second == d3.begin()+5 );
|
||||
|
||||
ret = extract_quoted_string(d4.begin(),d4.end());
|
||||
BOOST_CHECK( ret.first == "" );
|
||||
BOOST_CHECK( ret.first.empty() );
|
||||
BOOST_CHECK( ret.second == d4.begin() );
|
||||
|
||||
ret = extract_quoted_string(d5.begin(),d5.end());
|
||||
BOOST_CHECK( ret.first == "" );
|
||||
BOOST_CHECK( ret.first.empty() );
|
||||
BOOST_CHECK( ret.second == d5.begin() );
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE( extract_all_lws ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_attributes_blank ) {
|
||||
std::string s = "";
|
||||
std::string s;
|
||||
|
||||
websocketpp::http::attribute_list a;
|
||||
std::string::const_iterator it;
|
||||
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE( extract_attributes_simple ) {
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_parameters ) {
|
||||
std::string s1 = "";
|
||||
std::string s1;
|
||||
std::string s2 = "foo";
|
||||
std::string s3 = " foo \r\nAbc";
|
||||
std::string s4 = " \r\n foo ";
|
||||
@@ -410,7 +410,7 @@ BOOST_AUTO_TEST_CASE( case_insensitive_headers_overwrite ) {
|
||||
BOOST_AUTO_TEST_CASE( blank_consume ) {
|
||||
websocketpp::http::parser::request r;
|
||||
|
||||
std::string raw = "";
|
||||
std::string raw;
|
||||
|
||||
bool exception = false;
|
||||
|
||||
@@ -506,6 +506,18 @@ BOOST_AUTO_TEST_CASE( basic_request_with_body ) {
|
||||
BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" );
|
||||
BOOST_CHECK_EQUAL( r.get_header("Content-Length"), "5" );
|
||||
BOOST_CHECK_EQUAL( r.get_body(), "abcde" );
|
||||
|
||||
BOOST_CHECK_EQUAL( r.get_headers().size(), 2);
|
||||
|
||||
websocketpp::http::parser::header_list::const_iterator it = r.get_headers().begin();
|
||||
|
||||
BOOST_CHECK_EQUAL( it->first, "Content-Length");
|
||||
BOOST_CHECK_EQUAL( it->second, "5");
|
||||
|
||||
it++;
|
||||
|
||||
BOOST_CHECK_EQUAL( it->first, "Host");
|
||||
BOOST_CHECK_EQUAL( it->second, "www.example.com");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( basic_request_with_body_split ) {
|
||||
|
||||
@@ -4,7 +4,7 @@ file (GLOB HEADER_FILES *.hpp)
|
||||
|
||||
init_target (test_logger)
|
||||
|
||||
build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
build_test (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
link_boost ()
|
||||
final_target ()
|
||||
|
||||
@@ -11,7 +11,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE message.cpp)
|
||||
|
||||
init_target (test_message_buffer)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
file (GLOB SOURCE processor.cpp)
|
||||
|
||||
init_target (test_processor)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -11,7 +11,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE hybi00.cpp)
|
||||
|
||||
init_target (test_processor_hybi00)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -20,7 +20,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE hybi07.cpp)
|
||||
|
||||
init_target (test_processor_hybi07)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -29,7 +29,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE hybi08.cpp)
|
||||
|
||||
init_target (test_processor_hybi08)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -40,7 +40,7 @@ if (ZLIB_FOUND)
|
||||
file (GLOB SOURCE hybi13.cpp)
|
||||
|
||||
init_target (test_processor_hybi13)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
link_zlib()
|
||||
final_target ()
|
||||
@@ -50,7 +50,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE extension_permessage_compress.cpp)
|
||||
|
||||
init_target (test_processor_extension_permessage_compress)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
link_zlib()
|
||||
final_target ()
|
||||
|
||||
@@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE( deflate_init ) {
|
||||
<< websocketpp::utility::to_hex(test_out,test_out_size) << std::endl;
|
||||
|
||||
std::string input = "Hello";
|
||||
std::string output = "";
|
||||
std::string output;
|
||||
ec = de.compress(input,output);
|
||||
|
||||
BOOST_CHECK( ec == processor::extensions::error::uninitialized );
|
||||
@@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE( deflate_init ) {
|
||||
<< websocketpp::utility::to_hex(input) << std::endl
|
||||
<< websocketpp::utility::to_hex(output) << std::endl;
|
||||
|
||||
output = "";
|
||||
output.clear();
|
||||
|
||||
ec = de.compress(input,output);
|
||||
std::cout << ec.message() << std::endl
|
||||
@@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE( deflate_init ) {
|
||||
<< websocketpp::utility::to_hex(output) << std::endl;
|
||||
|
||||
input = output;
|
||||
output = "";
|
||||
output.clear();
|
||||
ec = de.decompress(input,output);
|
||||
std::cout << ec.message() << std::endl
|
||||
<< websocketpp::utility::to_hex(input) << std::endl
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
file (GLOB SOURCE none.cpp)
|
||||
|
||||
init_target (test_random_none)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -11,7 +11,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE random_device.cpp)
|
||||
|
||||
init_target (test_random_random_device)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
file (GLOB SOURCE client.cpp)
|
||||
|
||||
init_target (test_roles_client)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -11,7 +11,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE server.cpp)
|
||||
|
||||
init_target (test_roles_server)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
|
||||
@@ -103,7 +103,7 @@ bool validate_func_subprotocol(server* s, std::string* out, std::string accept,
|
||||
|
||||
*out = o.str();
|
||||
|
||||
if (accept != "") {
|
||||
if (!accept.empty()) {
|
||||
con->select_subprotocol(accept);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ if (OPENSSL_FOUND)
|
||||
file (GLOB SOURCE integration.cpp)
|
||||
|
||||
init_target (test_transport)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
link_openssl()
|
||||
final_target ()
|
||||
@@ -14,7 +14,17 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE asio/timers.cpp)
|
||||
|
||||
init_target (test_transport_asio_timers)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
link_openssl()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
|
||||
# Test transport asio security
|
||||
file (GLOB SOURCE asio/security.cpp)
|
||||
|
||||
init_target (test_transport_asio_security)
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
link_openssl()
|
||||
final_target ()
|
||||
@@ -26,7 +36,7 @@ endif()
|
||||
file (GLOB SOURCE iostream/base.cpp)
|
||||
|
||||
init_target (test_transport_iostream_base)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -35,7 +45,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE iostream/endpoint.cpp)
|
||||
|
||||
init_target (test_transport_iostream_endpoint)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -44,7 +54,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE iostream/connection.cpp)
|
||||
|
||||
init_target (test_transport_iostream_connection)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -53,7 +63,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE asio/base.cpp)
|
||||
|
||||
init_target (test_transport_asio_base)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
|
||||
@@ -15,14 +15,18 @@ BOOST_LIBS = boostlibs(['unit_test_framework','system','thread','chrono'],env) +
|
||||
|
||||
objs = env.Object('base_boost.o', ["base.cpp"], LIBS = BOOST_LIBS)
|
||||
objs += env.Object('timers_boost.o', ["timers.cpp"], LIBS = BOOST_LIBS)
|
||||
objs += env.Object('security_boost.o', ["security.cpp"], LIBS = BOOST_LIBS)
|
||||
prgs = env.Program('test_base_boost', ["base_boost.o"], LIBS = BOOST_LIBS)
|
||||
prgs += env.Program('test_timers_boost', ["timers_boost.o"], LIBS = BOOST_LIBS)
|
||||
prgs += env.Program('test_security_boost', ["security_boost.o"], LIBS = BOOST_LIBS)
|
||||
|
||||
if env_cpp11.has_key('WSPP_CPP11_ENABLED'):
|
||||
BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework','system'],env_cpp11) + [platform_libs] + [polyfill_libs] + [tls_libs]
|
||||
objs += env_cpp11.Object('base_stl.o', ["base.cpp"], LIBS = BOOST_LIBS_CPP11)
|
||||
objs += env_cpp11.Object('timers_stl.o', ["timers.cpp"], LIBS = BOOST_LIBS_CPP11)
|
||||
objs += env_cpp11.Object('security_stl.o', ["security.cpp"], LIBS = BOOST_LIBS_CPP11)
|
||||
prgs += env_cpp11.Program('test_base_stl', ["base_stl.o"], LIBS = BOOST_LIBS_CPP11)
|
||||
prgs += env_cpp11.Program('test_timers_stl', ["timers_stl.o"], LIBS = BOOST_LIBS_CPP11)
|
||||
prgs += env_cpp11.Program('test_security_stl', ["security_stl.o"], LIBS = BOOST_LIBS_CPP11)
|
||||
|
||||
Return('prgs')
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
//#define BOOST_TEST_DYN_LINK
|
||||
#define BOOST_TEST_MODULE transport_asio_base
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <websocketpp/common/type_traits.hpp>
|
||||
|
||||
#include <websocketpp/transport/asio/security/none.hpp>
|
||||
#include <websocketpp/transport/asio/security/tls.hpp>
|
||||
|
||||
template <typename base>
|
||||
struct dummy_con : public base {
|
||||
websocketpp::lib::error_code test() {
|
||||
return this->translate_ec(websocketpp::lib::asio::error_code());
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE( translated_ec_none ) {
|
||||
dummy_con<websocketpp::transport::asio::basic_socket::connection> tscon;
|
||||
|
||||
// If the current configuration settings result in the library error type and the asio
|
||||
// error type being the same, then the code should pass through natively. Otherwise
|
||||
// we should get a generic pass through error.
|
||||
if(websocketpp::lib::is_same<websocketpp::lib::error_code,websocketpp::lib::asio::error_code>::value) {
|
||||
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::lib::error_code() );
|
||||
} else {
|
||||
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::transport::error::make_error_code(websocketpp::transport::error::pass_through) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( translated_ec_tls ) {
|
||||
dummy_con<websocketpp::transport::asio::tls_socket::connection> tscon;
|
||||
|
||||
// If the current configuration settings result in the library error type and the asio
|
||||
// error type being the same, then the code should pass through natively. Otherwise
|
||||
// we should get a generic pass through error.
|
||||
if(websocketpp::lib::is_same<websocketpp::lib::error_code,websocketpp::lib::asio::error_code>::value) {
|
||||
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::lib::error_code() );
|
||||
} else {
|
||||
BOOST_CHECK_EQUAL( tscon.test(), websocketpp::transport::error::make_error_code(websocketpp::transport::error::pass_through) );
|
||||
}
|
||||
}
|
||||
@@ -108,14 +108,16 @@ struct config {
|
||||
// Mock context that does no validation
|
||||
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
|
||||
context_ptr on_tls_init(websocketpp::connection_hdl) {
|
||||
return context_ptr(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1));
|
||||
return context_ptr(new boost::asio::ssl::context(boost::asio::ssl::context::sslv23));
|
||||
}
|
||||
|
||||
// Mock connection
|
||||
struct mock_con: public websocketpp::transport::asio::connection<config> {
|
||||
typedef websocketpp::transport::asio::connection<config> base;
|
||||
|
||||
mock_con(bool a, config::alog_type& b, config::elog_type& c) : base(a,b,c) {}
|
||||
mock_con(bool a, const websocketpp::lib::shared_ptr<config::alog_type>& b,
|
||||
const websocketpp::lib::shared_ptr<config::elog_type>& c)
|
||||
: base(a,b,c) {}
|
||||
|
||||
void start() {
|
||||
base::init(websocketpp::lib::bind(&mock_con::handle_start,this,
|
||||
@@ -139,8 +141,8 @@ struct mock_endpoint : public websocketpp::transport::asio::endpoint<config> {
|
||||
typedef websocketpp::transport::asio::endpoint<config> base;
|
||||
|
||||
mock_endpoint() {
|
||||
alog.set_channels(websocketpp::log::alevel::all);
|
||||
base::init_logging(&alog,&elog);
|
||||
alog->set_channels(websocketpp::log::alevel::all);
|
||||
base::init_logging(alog,elog);
|
||||
init_asio();
|
||||
}
|
||||
|
||||
@@ -170,8 +172,8 @@ struct mock_endpoint : public websocketpp::transport::asio::endpoint<config> {
|
||||
}
|
||||
|
||||
connection_ptr m_con;
|
||||
config::alog_type alog;
|
||||
config::elog_type elog;
|
||||
websocketpp::lib::shared_ptr<config::alog_type> alog;
|
||||
websocketpp::lib::shared_ptr<config::elog_type> elog;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE( tls_handshake_timeout ) {
|
||||
|
||||
@@ -58,7 +58,7 @@ struct stub_con : public iostream_con {
|
||||
typedef websocketpp::lib::shared_ptr<type> ptr;
|
||||
typedef iostream_con::timer_ptr timer_ptr;
|
||||
|
||||
stub_con(bool is_server, config::alog_type & a, config::elog_type & e)
|
||||
stub_con(bool is_server, const websocketpp::lib::shared_ptr<config::alog_type>& a, const websocketpp::lib::shared_ptr<config::elog_type>& e)
|
||||
: iostream_con(is_server,a,e)
|
||||
// Set the error to a known code that is unused by the library
|
||||
// This way we can easily confirm that the handler was run at all.
|
||||
@@ -164,8 +164,8 @@ struct stub_con : public iostream_con {
|
||||
};
|
||||
|
||||
// Stubs
|
||||
config::alog_type alogger;
|
||||
config::elog_type elogger;
|
||||
websocketpp::lib::shared_ptr<config::alog_type> alogger = websocketpp::lib::make_shared<config::alog_type>();
|
||||
websocketpp::lib::shared_ptr<config::elog_type> elogger = websocketpp::lib::make_shared<config::elog_type>();
|
||||
|
||||
BOOST_AUTO_TEST_CASE( const_methods ) {
|
||||
iostream_con::ptr con(new iostream_con(true,alogger,elogger));
|
||||
@@ -580,6 +580,16 @@ BOOST_AUTO_TEST_CASE( shutdown_handler ) {
|
||||
BOOST_CHECK_EQUAL( con->ec, make_error_code(websocketpp::transport::error::general) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( clear_handler ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
|
||||
con->set_shutdown_handler(&sd_handler);
|
||||
con->set_shutdown_handler(NULL);
|
||||
BOOST_CHECK_EQUAL( con->ec, make_error_code(websocketpp::error::test) );
|
||||
con->shutdown();
|
||||
BOOST_CHECK_EQUAL( con->ec, websocketpp::lib::error_code() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( shared_pointer_memory_cleanup ) {
|
||||
stub_con::ptr con(new stub_con(true,alogger,elogger));
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
file (GLOB SOURCE close.cpp)
|
||||
|
||||
init_target (test_close)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -11,7 +11,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE error.cpp)
|
||||
|
||||
init_target (test_error)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -20,7 +20,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE frame.cpp)
|
||||
|
||||
init_target (test_frame)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -29,7 +29,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE sha1.cpp)
|
||||
|
||||
init_target (test_sha1)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -38,7 +38,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE uri.cpp)
|
||||
|
||||
init_target (test_uri)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
@@ -47,7 +47,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
file (GLOB SOURCE utilities.cpp)
|
||||
|
||||
init_target (test_utilities)
|
||||
build_executable (${TARGET_NAME} ${SOURCE})
|
||||
build_test (${TARGET_NAME} ${SOURCE})
|
||||
link_boost ()
|
||||
final_target ()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "test")
|
||||
|
||||
@@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE( value_extraction ) {
|
||||
|
||||
BOOST_AUTO_TEST_CASE( extract_empty ) {
|
||||
lib::error_code ec;
|
||||
std::string payload = "";
|
||||
std::string payload;
|
||||
|
||||
BOOST_CHECK( close::extract_code(payload,ec) == close::status::no_status );
|
||||
BOOST_CHECK( !ec );
|
||||
@@ -109,12 +109,12 @@ BOOST_AUTO_TEST_CASE( extract_reason ) {
|
||||
BOOST_CHECK( close::extract_reason(payload,ec) == "Foo" );
|
||||
BOOST_CHECK( !ec );
|
||||
|
||||
payload = "";
|
||||
BOOST_CHECK( close::extract_reason(payload,ec) == "" );
|
||||
payload.clear();
|
||||
BOOST_CHECK( close::extract_reason(payload,ec).empty() );
|
||||
BOOST_CHECK( !ec );
|
||||
|
||||
payload = "00";
|
||||
BOOST_CHECK( close::extract_reason(payload,ec) == "" );
|
||||
BOOST_CHECK( close::extract_reason(payload,ec).empty() );
|
||||
BOOST_CHECK( !ec );
|
||||
|
||||
payload = "000";
|
||||
|
||||
@@ -246,7 +246,7 @@ int main() {
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
std::string reason;
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
@@ -289,7 +289,7 @@ int main() {
|
||||
|
||||
std::string cmd;
|
||||
int id;
|
||||
std::string message = "";
|
||||
std::string message;
|
||||
|
||||
ss >> cmd >> id;
|
||||
std::getline(ss,message);
|
||||
@@ -301,7 +301,7 @@ int main() {
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
std::string reason;
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Utility Client Example Application
|
||||
==================================
|
||||
Utility Client Example Application Tutorial
|
||||
===========================================
|
||||
|
||||
Chapter 1: Initial Setup & Basics
|
||||
---------------------------------
|
||||
@@ -17,7 +17,7 @@ A basic program loop that prompts the user for a command and then processes it.
|
||||
|
||||
*note* A code snapshot for each step is present next to this tutorial file in the git repository.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
@@ -44,7 +44,7 @@ int main() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
### Step 2
|
||||
|
||||
@@ -61,7 +61,7 @@ Connections do not maintain a link back to their associated endpoint. Endpoints
|
||||
|
||||
WebSocket++ endpoints are built by combining an endpoint role with an endpoint config. There are two different types of endpoint roles, one each for the client and server roles in a WebSocket session. This is a client tutorial so we will use the client role `websocketpp::client` which is provided by the `<websocketpp/client.hpp>` header.
|
||||
|
||||
> ###### Terminology: Endpoint Config
|
||||
> ##### Terminology: Endpoint Config
|
||||
> WebSocket++ endpoints have a group of settings that may be configured at compile time via the `config` template parameter. A config is a struct that contains types and static constants that are used to produce an endpoint with specific properties. Depending on which config is being used the endpoint will have different methods available and may have additional third party dependencies.
|
||||
|
||||
The endpoint role takes a template parameter called `config` that is used to configure the behavior of endpoint at compile time. For this example we are going to use a default config provided by the library called `asio_client`, provided by `<websocketpp/config/asio_no_tls_client.hpp>`. This is a client config that uses boost::asio to provide network transport and does not support TLS based security. Later on we will discuss how to introduce TLS based security into a WebSocket++ application, more about the other stock configs, and how to build your own custom configs.
|
||||
@@ -78,7 +78,7 @@ In addition to the new headers, boost::asio depends on the `boost_system` shared
|
||||
`clang++ step2.cpp -lboost_system`
|
||||
|
||||
#### Code so far
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
@@ -110,8 +110,7 @@ int main() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
### Step 3
|
||||
|
||||
@@ -119,7 +118,7 @@ _Create endpoint wrapper object that handles initialization and setting up the b
|
||||
|
||||
In order to process user input while network processing occurs in the background we are going to use a separate thread for the WebSocket++ processing loop. This leaves the main thread free to process foreground user input. In order to enable simple RAII style resource management for our thread and endpoint we will use a wrapper object that configures them both in its constructor.
|
||||
|
||||
> ###### Terminology: websocketpp::lib namespace
|
||||
> ##### Terminology: websocketpp::lib namespace
|
||||
> WebSocket++ is designed to be used with a C++11 standard library. As this is not universally available in popular build systems the Boost libraries may be used as polyfills for the C++11 standard library in C++98 build environments. The `websocketpp::lib` namespace is used by the library and its associated examples to abstract away the distinctions between the two. `websocketpp::lib::shared_ptr` will evaluate to `std::shared_ptr` in a C++11 environment and `boost::shared_ptr` otherwise.
|
||||
>
|
||||
> This tutorial uses the `websocketpp::lib` wrappers because it doesn't know what the build environment of the reader is. For your applications, unless you are interested in similar portability, are free to use the boost or std versions of these types directly.
|
||||
@@ -129,21 +128,21 @@ In order to process user input while network processing occurs in the background
|
||||
Within the `websocket_endpoint` constructor several things happen:
|
||||
|
||||
First, we set the endpoint logging behavior to silent by clearing all of the access and error logging channels. [TODO: link to more information about logging]
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
|
||||
```
|
||||
~~~
|
||||
|
||||
Next, we initialize the transport system underlying the endpoint and set it to perpetual mode. In perpetual mode the endpoint's processing loop will not exit automatically when it has no connections. This is important because we want this endpoint to remain active while our application is running and process requests for new WebSocket connections on demand as we need them. Both of these methods are specific to the asio transport. They will not be necessary or present in endpoints that use a non-asio config.
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
m_endpoint.init_asio();
|
||||
m_endpoint.start_perpetual();
|
||||
```
|
||||
~~~
|
||||
|
||||
Finally, we launch a thread to run the `run` method of our client endpoint. While the endpoint is running it will process connection tasks (read and deliver incoming messages, frame and send outgoing messages, etc). Because it is running in perpetual mode, when there are no connections active it will wait for a new connection.
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Build
|
||||
|
||||
@@ -163,7 +162,7 @@ Now that our client endpoint template is actually instantiated a few more linker
|
||||
|
||||
#### Code so far
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
@@ -215,7 +214,7 @@ int main() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
### Step 4
|
||||
|
||||
@@ -234,14 +233,14 @@ The `websocket_endpoint` object has gained some new data members and methods. It
|
||||
#### The connect method
|
||||
A new WebSocket connection is initiated via a three step process. First, a connection request is created by `endpoint::get_connection(uri)`. Next, the connection request is configured. Lastly, the connection request is submitted back to the endpoint via `endpoint::connect()` which adds it to the queue of new connections to make.
|
||||
|
||||
> ###### Terminology `connection_ptr`
|
||||
> ##### Terminology `connection_ptr`
|
||||
> WebSocket++ keeps track of connection related resources using a reference counted shared pointer. The type of this pointer is `endpoint::connection_ptr`. A `connection_ptr` allows direct access to information about the connection and allows changing connection settings. Because of this direct access and their internal resource management role within the library it is not safe for end applications to use `connection_ptr` except in the specific circumstances detailed below.
|
||||
>
|
||||
> **When is it safe to use `connection_ptr`?**
|
||||
> - After `endpoint::get_connection(...)` and before `endpoint::connect()`: `get_connection` returns a `connection_ptr`. It is safe to use this pointer to configure your new connection. Once you submit the connection to `connect` you may no longer use the `connection_ptr` and should discard it immediately for optimal memory management.
|
||||
> - During a handler: WebSocket++ allows you to register hooks / callbacks / event handlers for specific events that happen during a connection's lifetime. During the invocation of one of these handlers the library guarantees that it is safe to use a `connection_ptr` for the connection associated with the currently running handler.
|
||||
|
||||
> ###### Terminology `connection_hdl`
|
||||
> ##### Terminology `connection_hdl`
|
||||
> Because of the limited thread safety of the `connection_ptr` the library also provides a more flexible connection identifier, the `connection_hdl`. The `connection_hdl` has type `websocketpp::connection_hdl` and it is defined in `<websocketpp/common/connection_hdl.hpp>`. Note that unlike `connection_ptr` this is not dependent on the type or config of the endpoint. Code that simply stores or transmits `connection_hdl` but does not use them can include only the header above and can treat its hdls like values.
|
||||
>
|
||||
> Connection handles are not used directly. They are used by endpoint methods to identify the target of the desired action. For example, the endpoint method that sends a new message will take as a parameter the hdl of the connection to send the message to.
|
||||
@@ -267,15 +266,15 @@ A new WebSocket connection is initiated via a three step process. First, a conne
|
||||
|
||||
If connection creation succeeds, the next sequential connection ID is generated and a `connection_metadata` object is inserted into the connection list under that ID. Initially the metadata object stores the connection ID, the `connection_hdl`, and the URI the connection was opened to.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
int new_id = m_next_id++;
|
||||
metadata_ptr metadata(new connection_metadata(new_id, con->get_handle(), uri));
|
||||
m_connection_list[new_id] = metadata;
|
||||
```
|
||||
~~~
|
||||
|
||||
Next, the connection request is configured. For this step the only configuration we will do is setting up a few default handlers. Later on we will return and demonstrate some more detailed configuration that can happen here (setting user agents, origin, proxies, custom headers, subprotocols, etc).
|
||||
|
||||
> ###### Terminology: Registering handlers
|
||||
> ##### Terminology: Registering handlers
|
||||
> WebSocket++ provides a number of execution points where you can register to have a handler run. Which of these points are available to your endpoint will depend on its config. TLS handlers will not exist on non-TLS endpoints for example. A complete list of handlers can be found at http://www.zaphoyd.com/websocketpp/manual/reference/handler-list.
|
||||
>
|
||||
> Handlers can be registered at the endpoint level and at the connection level. Endpoint handlers are copied into new connections as they are created. Changing an endpoint handler will affect only future connections. Handlers registered at the connection level will be bound to that specific connection only.
|
||||
@@ -292,14 +291,14 @@ In this example we are going to set connection specific handlers that are bound
|
||||
|
||||
Lets look at the parameters being sent to bind in detail:
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
con->set_open_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_open,
|
||||
metadata,
|
||||
&m_endpoint,
|
||||
websocketpp::lib::placeholders::_1
|
||||
));
|
||||
```
|
||||
~~~
|
||||
|
||||
`&connection_metadata::on_open` is the address of the `on_open` member function of the `connection_metadata` class. `metadata_ptr` is a pointer to the `connection_metadata` object associated with this class. It will be used as the object on which the `on_open` member function will be called. `&m_endpoint` is the address of the endpoint in use. This parameter will be passed as-is to the `on_open` method. Lastly, `websocketpp::lib::placeholders::_1` is a placeholder indicating that the bound function should take one additional argument to be filled in at a later time. WebSocket++ will fill in this placeholder with the `connection_hdl` when it invokes the handler.
|
||||
|
||||
@@ -315,7 +314,7 @@ The fail handler we registered, `connection_metadata::on_fail`, sets the status
|
||||
|
||||
Two new commands have been set up. "connect [uri]" will pass the URI to the `websocket_endpoint` connect method and report an error or the connection ID of the new connection. "show [connection id]" will retrieve and print out the metadata associated with that connection. The help text has been updated accordingly.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
} else if (input.substr(0,7) == "connect") {
|
||||
int id = endpoint.connect(input.substr(8));
|
||||
if (id != -1) {
|
||||
@@ -331,7 +330,7 @@ Two new commands have been set up. "connect [uri]" will pass the URI to the `web
|
||||
std::cout << "> Unknown connection id " << id << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Build
|
||||
|
||||
@@ -339,7 +338,7 @@ There are no changes to the build instructions from step 3
|
||||
|
||||
#### Run
|
||||
|
||||
```
|
||||
~~~
|
||||
Enter Command: connect not a websocket uri
|
||||
> Connect initialization error: invalid uri
|
||||
Enter Command: show 0
|
||||
@@ -358,11 +357,11 @@ Enter Command: show 1
|
||||
> Status: Failed
|
||||
> Remote Server: Apache
|
||||
> Error/close reason: Invalid HTTP status.
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Code so far
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
@@ -525,7 +524,7 @@ int main() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
### Step 5
|
||||
|
||||
@@ -535,7 +534,7 @@ This step adds a command that allows you to close a WebSocket connection and adj
|
||||
|
||||
#### Getting connection close information out of WebSocket++
|
||||
|
||||
> ###### Terminology: WebSocket close codes & reasons
|
||||
> ##### Terminology: WebSocket close codes & reasons
|
||||
> The WebSocket close handshake involves an exchange of optional machine readable close codes and human readable reason strings. Each endpoint sends independent close details. The codes are short integers. The reasons are UTF8 text strings of at most 125 characters. More details about valid close code ranges and the meaning of each code can be found at https://tools.ietf.org/html/rfc6455#section-7.4
|
||||
|
||||
The `websocketpp::close::status` namespace contains named constants for all of the IANA defined close codes. It also includes free functions to determine whether a value is reserved or invalid and to convert a code to a human readable text representation.
|
||||
@@ -554,7 +553,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
|
||||
~~~{.cpp}
|
||||
void on_close(client * c, websocketpp::connection_hdl hdl) {
|
||||
m_status = "Closed";
|
||||
client::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
@@ -564,7 +563,7 @@ void on_close(client * c, websocketpp::connection_hdl hdl) {
|
||||
<< "), close reason: " << con->get_remote_close_reason();
|
||||
m_error_reason = s.str();
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
Similarly to `on_open` and `on_fail`, `websocket_endpoint::connect` registers this close handler when a new connection is made.
|
||||
|
||||
@@ -572,7 +571,7 @@ Similarly to `on_open` and `on_fail`, `websocket_endpoint::connect` registers th
|
||||
|
||||
This method starts by looking up the given connection ID in the connection list. Next a close request is sent to the connection's handle with the specified WebSocket close code. This is done by calling `endpoint::close`. This is a thread safe method that is used to asynchronously dispatch a close signal to the connection with the given handle. When the operation is complete the connection's close handler will be triggered.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
void close(int id, websocketpp::close::status::value code) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
@@ -587,7 +586,7 @@ void close(int id, websocketpp::close::status::value code) {
|
||||
std::cout << "> Error initiating close: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Add close option to the command loop and help message
|
||||
|
||||
@@ -595,21 +594,21 @@ A close option is added to the command loop. It takes a connection ID and option
|
||||
|
||||
An entry is also added to the help system to describe how the new command may be used.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
else if (input.substr(0,5) == "close") {
|
||||
std::stringstream ss(input);
|
||||
|
||||
std::string cmd;
|
||||
int id;
|
||||
int close_code = websocketpp::close::status::normal;
|
||||
std::string reason = "";
|
||||
std::string reason;
|
||||
|
||||
ss >> cmd >> id >> close_code;
|
||||
std::getline(ss,reason);
|
||||
|
||||
endpoint.close(id, close_code, reason);
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Close all outstanding connections in `websocket_endpoint` destructor
|
||||
|
||||
@@ -617,7 +616,7 @@ Until now quitting the program left outstanding connections and the WebSocket++
|
||||
|
||||
The destructor for `websocket_endpoint` now stops perpetual mode (so the run thread exits after the last connection is closed) and iterates through the list of open connections and requests a clean close for each. Finally, the run thread is joined which causes the program to wait until those connection closes complete.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
~websocket_endpoint() {
|
||||
m_endpoint.stop_perpetual();
|
||||
|
||||
@@ -639,7 +638,7 @@ The destructor for `websocket_endpoint` now stops perpetual mode (so the run thr
|
||||
|
||||
m_thread->join();
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Build
|
||||
|
||||
@@ -647,7 +646,7 @@ There are no changes to the build instructions from step 4
|
||||
|
||||
#### Run
|
||||
|
||||
```
|
||||
~~~
|
||||
Enter Command: connect ws://localhost:9002
|
||||
> Created connection with id 0
|
||||
Enter Command: close 0 1001 example message
|
||||
@@ -662,7 +661,7 @@ Enter Command: close 1 1006
|
||||
> Error initiating close: Invalid close code used
|
||||
Enter Command: quit
|
||||
> Closing connection 1
|
||||
```
|
||||
~~~
|
||||
|
||||
### Step 6
|
||||
|
||||
@@ -670,7 +669,7 @@ _Sending and receiving messages_
|
||||
|
||||
This step adds a command to send a message on a given connection and updates the show command to print a transcript of all sent and received messages for that connection.
|
||||
|
||||
> ###### Terminology: WebSocket message types (opcodes)
|
||||
> ##### Terminology: WebSocket message types (opcodes)
|
||||
> WebSocket messages have types indicated by their opcode. The protocol currently specifies two different opcodes for data messages, text and binary. Text messages represent UTF8 text and will be validated as such. Binary messages represent raw binary bytes and are passed through directly with no validation.
|
||||
>
|
||||
> WebSocket++ provides the values `websocketpp::frame::opcode::text` and `websocketpp::frame::opcode::binary` that can be used to direct how outgoing messages should be sent and to check how incoming messages are formatted.
|
||||
@@ -698,7 +697,7 @@ The third overload, `connection_hdl hdl, message_ptr msg`, takes a WebSocket++ `
|
||||
|
||||
Like the close method, send will start by looking up the given connection ID in the connection list. Next a send request is sent to the connection's handle with the specified WebSocket message and the text opcode. Finally, we record the sent message with our connection metadata object so later our show connection command can print a list of messages sent.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
void send(int id, std::string message) {
|
||||
websocketpp::lib::error_code ec;
|
||||
|
||||
@@ -716,13 +715,13 @@ void send(int id, std::string message) {
|
||||
|
||||
metadata_it->second->record_sent_message(message);
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Add send option to the command loop and help message
|
||||
|
||||
A send option is added to the command loop. It takes a connection ID and a text message to send. An entry is also added to the help system to describe how the new command may be used.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
else if (input.substr(0,4) == "send") {
|
||||
std::stringstream ss(input);
|
||||
|
||||
@@ -735,28 +734,28 @@ else if (input.substr(0,4) == "send") {
|
||||
|
||||
endpoint.send(id, message);
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Add glue to `connection_metadata` for storing sent messages
|
||||
|
||||
In order to store messages sent on this connection some code is added to `connection_metadata`. This includes a new data member `std::vector<std::string> m_messages` to keep track of all messages sent and received as well as a method for adding a sent message in that list:
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
void record_sent_message(std::string message) {
|
||||
m_messages.push_back(">> " + message);
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
Finally the connection metadata output operator is updated to also print a list of processed messages:
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
out << "> Messages Processed: (" << data.m_messages.size() << ") \n";
|
||||
|
||||
std::vector<std::string>::const_iterator it;
|
||||
for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) {
|
||||
out << *it << "\n";
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Receiving Messages
|
||||
|
||||
@@ -766,7 +765,7 @@ Messages are received by registering a message handler. This handler will be cal
|
||||
|
||||
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
|
||||
~~~{.cpp}
|
||||
void on_message(websocketpp::connection_hdl hdl, client::message_ptr msg) {
|
||||
if (msg->get_opcode() == websocketpp::frame::opcode::text) {
|
||||
m_messages.push_back(msg->get_payload());
|
||||
@@ -774,18 +773,18 @@ void on_message(websocketpp::connection_hdl hdl, client::message_ptr msg) {
|
||||
m_messages.push_back(websocketpp::utility::to_hex(msg->get_payload()));
|
||||
}
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
In order to have this handler called when new messages are received we also register it with our connection. Note that unlike most other handlers, the message handler has two parameters and thus needs two placeholders.
|
||||
|
||||
```cpp
|
||||
~~~{.cpp}
|
||||
con->set_message_handler(websocketpp::lib::bind(
|
||||
&connection_metadata::on_message,
|
||||
metadata_ptr,
|
||||
websocketpp::lib::placeholders::_1,
|
||||
websocketpp::lib::placeholders::_2
|
||||
));
|
||||
```
|
||||
~~~
|
||||
|
||||
#### Build
|
||||
|
||||
@@ -795,7 +794,7 @@ There are no changes to the build instructions from step 5
|
||||
|
||||
In this example run we are connecting to the WebSocket++ example echo_server. This server will repeat any message we send back to it. You can also try testing this with the echo server at `ws://echo.websocket.org` with similar results.
|
||||
|
||||
```
|
||||
~~~
|
||||
Enter Command: connect ws://localhost:9002
|
||||
> Created connection with id 0
|
||||
Enter Command: send 0 example message
|
||||
@@ -807,7 +806,7 @@ Enter Command: show 0
|
||||
> Messages Processed: (2)
|
||||
>> example message
|
||||
<< example message
|
||||
```
|
||||
~~~
|
||||
|
||||
### Step 7
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility server tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_server
|
||||
// directory of the WebSocket++ repository.
|
||||
|
||||
// The ASIO_STANDALONE define is necessary to use the standalone version of Asio.
|
||||
// Remove if you are using Boost Asio.
|
||||
#define ASIO_STANDALONE
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
|
||||
class utility_server {
|
||||
public:
|
||||
utility_server() {
|
||||
// Set logging settings
|
||||
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
|
||||
|
||||
// Initialize Asio
|
||||
m_endpoint.init_asio();
|
||||
}
|
||||
|
||||
void run() {
|
||||
// Listen on port 9002
|
||||
m_endpoint.listen(9002);
|
||||
|
||||
// Queues a connection accept operation
|
||||
m_endpoint.start_accept();
|
||||
|
||||
// Start the Asio io_service run loop
|
||||
m_endpoint.run();
|
||||
}
|
||||
private:
|
||||
server m_endpoint;
|
||||
};
|
||||
|
||||
int main() {
|
||||
utility_server s;
|
||||
s.run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// **NOTE:** This file is a snapshot of the WebSocket++ utility server tutorial.
|
||||
// Additional related material can be found in the tutorials/utility_server
|
||||
// directory of the WebSocket++ repository.
|
||||
|
||||
// The ASIO_STANDALONE define is necessary to use the standalone version of Asio.
|
||||
// Remove if you are using Boost Asio.
|
||||
#define ASIO_STANDALONE
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
|
||||
class utility_server {
|
||||
public:
|
||||
utility_server() {
|
||||
// Set logging settings
|
||||
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
|
||||
|
||||
// Initialize Asio
|
||||
m_endpoint.init_asio();
|
||||
|
||||
// Set the default message handler to the echo handler
|
||||
m_endpoint.set_message_handler(std::bind(
|
||||
&utility_server::echo_handler, this,
|
||||
std::placeholders::_1, std::placeholders::_2
|
||||
));
|
||||
}
|
||||
|
||||
void echo_handler(websocketpp::connection_hdl hdl, server::message_ptr msg) {
|
||||
// write a new message
|
||||
m_endpoint.send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}
|
||||
|
||||
void run() {
|
||||
// Listen on port 9002
|
||||
m_endpoint.listen(9002);
|
||||
|
||||
// Queues a connection accept operation
|
||||
m_endpoint.start_accept();
|
||||
|
||||
// Start the Asio io_service run loop
|
||||
m_endpoint.run();
|
||||
}
|
||||
private:
|
||||
server m_endpoint;
|
||||
};
|
||||
|
||||
int main() {
|
||||
utility_server s;
|
||||
s.run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
Utility Server Example Application Tutorial
|
||||
===========================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial provides a step by step discussion of building a basic WebSocket++ server. The final product of this tutorial is the utility_server example application from the example section. This server demonstrates the following features:
|
||||
|
||||
- Use Asio Transport for networking
|
||||
- Accept multiple WebSocket connections at once
|
||||
- Read incoming messages and perform a few basic actions (echo, broadcast, telemetry, server commands) based on the path
|
||||
- Use validate handler to reject connections to invalid paths
|
||||
- Serve basic HTTP responses with the http handler
|
||||
- Gracefully exit the server
|
||||
- Encrypt connections with TLS
|
||||
|
||||
This tutorial is current as of the 0.6.x version of the library.
|
||||
|
||||
Chapter 1: Initial Setup & Basics
|
||||
---------------------------------
|
||||
|
||||
### Step 1
|
||||
|
||||
_Add WebSocket++ includes and set up a a server endpoint type._
|
||||
|
||||
WebSocket++ includes two major object types. The endpoint and the connection. The
|
||||
endpoint creates and launches new connections and maintains default settings for
|
||||
those connections. Endpoints also manage any shared network resources.
|
||||
|
||||
The connection stores information specific to each WebSocket session.
|
||||
|
||||
> **Note:** Once a connection is launched, there is no link between the endpoint and the connection. All default settings are copied into the new connection by the endpoint. Changing default settings on an endpoint will only affect future connections.
|
||||
Connections do not maintain a link back to their associated endpoint. Endpoints do not maintain a list of outstanding connections. If your application needs to iterate over all connections it will need to maintain a list of them itself.
|
||||
|
||||
WebSocket++ endpoints are built by combining an endpoint role with an endpoint config. There are two different types of endpoint roles, one each for the client and server roles in a WebSocket session. This is a server tutorial so we will use the server role `websocketpp::server` which is provided by the `<websocketpp/server.hpp>` header.
|
||||
|
||||
> #### Terminology: Endpoint Config
|
||||
> WebSocket++ endpoints have a group of settings that may be configured at compile time via the `config` template parameter. A config is a struct that contains types and static constants that are used to produce an endpoint with specific properties. Depending on which config is being used the endpoint will have different methods available and may have additional third party dependencies.
|
||||
|
||||
The endpoint role takes a template parameter called `config` that is used to configure the behavior of endpoint at compile time. For this example we are going to use a default config provided by the library called `asio`, provided by `<websocketpp/config/asio_no_tls.hpp>`. This is a server config that uses the Asio library to provide network transport and does not support TLS based security. Later on we will discuss how to introduce TLS based security into a WebSocket++ application, more about the other stock configs, and how to build your own custom configs.
|
||||
|
||||
Combine a config with an endpoint role to produce a fully configured endpoint. This type will be used frequently so I would recommend a typedef here.
|
||||
|
||||
`typedef websocketpp::server<websocketpp::config::asio> server`
|
||||
|
||||
#### `utility_server` constructor
|
||||
|
||||
This endpoint type will be the base of the utility_server object that will keep track of the state of the server. Within the `utility_server` constructor several things happen:
|
||||
|
||||
First, we adjust the endpoint logging behavior to include all error logging channels and all access logging channels except the frame payload, which is particularly noisy and generally useful only for debugging. [TODO: link to more information about logging]
|
||||
|
||||
~~~{.cpp}
|
||||
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
|
||||
~~~
|
||||
|
||||
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?]
|
||||
|
||||
~~~{.cpp}
|
||||
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.
|
||||
|
||||
~~~{.cpp}
|
||||
// Listen on port 9002
|
||||
m_endpoint.listen(9002);
|
||||
|
||||
// Queues a connection accept operation
|
||||
m_endpoint.start_accept();
|
||||
|
||||
// Start the Asio io_service run loop
|
||||
m_endpoint.run();
|
||||
~~~
|
||||
|
||||
The final line, `m_endpoint.run();`, will block until the endpoint is instructed to stop listening for new connections. While running it will listen for and process new connections as well as accept and process new data and control messages for existing connections. WebSocket++ uses Asio in an asyncronous mode where multiple connections can be similtaneously serviced efficiently within a single thread.
|
||||
|
||||
#### Build
|
||||
Adding WebSocket++ has added a few dependencies to our program that must be addressed in the build system. Firstly, the WebSocket++ library headers need must be in the include search path of your build system. How exactly this is done depends on where you have the WebSocket++ headers installed what build system you are using.
|
||||
|
||||
For the rest of this tutorial we are going to assume a C++11 build environment. WebSocket++ will work with pre-C++11 systems if your build system has access to a recent version of the Boost library headers.
|
||||
|
||||
Finally, to use the Asio transport config we need to bring in the Asio library. There are two options here. If you have access to a C++11 build environment the standalone version from http://think-async.com is a good option. This header only library does not bring in any special dependencies and ensures you have the latest version of Asio. If you do not have a C++11 build environment or already have brought in the Boost libraries you can also use the version of Asio bundled with Boost.
|
||||
|
||||
To use standalone Asio, make sure the Asio headers are in your include path and define ASIO_STANDALONE. To use Boost Asio, make sure the Boost headers are in your include path and that you are linking to the boost_system library.
|
||||
|
||||
`c++ -std=c++11 step1.cpp` (Asio Standalone)
|
||||
OR
|
||||
`c++ -std=c++11 step1.cpp -lboost_system` (Boost Asio)
|
||||
|
||||
#### Code so far
|
||||
```cpp
|
||||
// The ASIO_STANDALONE define is necessary to use the standalone version of Asio.
|
||||
// Remove if you are using Boost Asio.
|
||||
#define ASIO_STANDALONE
|
||||
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
|
||||
class utility_server {
|
||||
public:
|
||||
utility_server() {
|
||||
// Set logging settings
|
||||
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
|
||||
|
||||
// Initialize Asio
|
||||
m_endpoint.init_asio();
|
||||
}
|
||||
|
||||
void run() {
|
||||
// Listen on port 9002
|
||||
m_endpoint.listen(9002);
|
||||
|
||||
// Queues a connection accept operation
|
||||
m_endpoint.start_accept();
|
||||
|
||||
// Start the Asio io_service run loop
|
||||
m_endpoint.run();
|
||||
}
|
||||
private:
|
||||
server m_endpoint;
|
||||
};
|
||||
|
||||
int main() {
|
||||
utility_server s;
|
||||
s.run();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2
|
||||
|
||||
_Set up a message handler to echo all replies back to the original user_
|
||||
|
||||
#### Setting a message handler
|
||||
|
||||
> ###### Terminology: Registering handlers
|
||||
> WebSocket++ provides a number of execution points where you can register to have a handler run. Which of these points are available to your endpoint will depend on its config. TLS handlers will not exist on non-TLS endpoints for example. A complete list of handlers can be found at http://www.zaphoyd.com/websocketpp/manual/reference/handler-list.
|
||||
>
|
||||
> Handlers can be registered at the endpoint level and at the connection level. Endpoint handlers are copied into new connections as they are created. Changing an endpoint handler will affect only future connections. Handlers registered at the connection level will be bound to that specific connection only.
|
||||
>
|
||||
> The signature of handler binding methods is the same for endpoints and connections. The format is: `set_*_handler(...)`. Where * is the name of the handler. For example, `set_open_handler(...)` will set the handler to be called when a new connection is open. `set_fail_handler(...)` will set the handler to be called when a connection fails to connect.
|
||||
>
|
||||
> All handlers take one argument, a callable type that can be converted to a `std::function` with the correct count and type of arguments. You can pass free functions, functors, and Lambdas with matching argument lists as handlers. In addition, you can use `std::bind` (or `boost::bind`) to register functions with non-matching argument lists. This is useful for passing additional parameters not present in the handler signature or member functions that need to carry a 'this' pointer.
|
||||
>
|
||||
> The function signature of each handler can be looked up in the list above in the manual. In general, all handlers include the `connection_hdl` identifying which connection this even is associated with as the first parameter. Some handlers (such as the message handler) include additional parameters. Most handlers have a void return value but some (`validate`, `ping`, `tls_init`) do not. The specific meanings of the return values are documented in the handler list linked above.
|
||||
|
||||
|
||||
### Step 3
|
||||
|
||||
_error handling_
|
||||
|
||||
|
||||
### Step 4
|
||||
|
||||
_Set up open and close handlers and a connection data structure_
|
||||
|
||||
### Step 5
|
||||
|
||||
_Change the message handler for connections based on URI and add a validate handler to reject invalid URIs_
|
||||
|
||||
### Step 6
|
||||
|
||||
_Add some Admin commands (report total clients, cleanly shut down server)_
|
||||
|
||||
### Step 7
|
||||
|
||||
_Add some Broadcast commands_
|
||||
|
||||
### Step 8
|
||||
|
||||
_Add TLS_
|
||||
@@ -3,5 +3,12 @@
|
||||
# WEBSOCKETPP_FOUND - indicates that the module was found
|
||||
# WEBSOCKETPP_INCLUDE_DIR - include directories
|
||||
|
||||
@PACKAGE_INIT@
|
||||
set_and_check(WEBSOCKETPP_INCLUDE_DIR "@PACKAGE_INSTALL_INCLUDE_DIR@")
|
||||
set(WEBSOCKETPP_FOUND TRUE)
|
||||
set(WEBSOCKETPP_INCLUDE_DIR "@INSTALL_INCLUDE_DIR@")
|
||||
|
||||
#This is a bit of a hack, but it works well. It also allows continued support of CMake 2.8
|
||||
if(${CMAKE_VERSION} VERSION_GREATER 3.0.0 OR ${CMAKE_VERSION} VERSION_EQUAL 3.0.0)
|
||||
add_library(websocketpp::websocketpp INTERFACE IMPORTED)
|
||||
set_target_properties(websocketpp::websocketpp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WEBSOCKETPP_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
set(PACKAGE_VERSION "@WEBSOCKETPP_VERSION@")
|
||||
|
||||
# Check whether the requested PACKAGE_FIND_VERSION is compatible
|
||||
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
else()
|
||||
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
|
||||
set(PACKAGE_VERSION_EXACT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
@@ -322,7 +322,7 @@ inline status::value extract_code(std::string const & payload, lib::error_code
|
||||
inline std::string extract_reason(std::string const & payload, lib::error_code
|
||||
& ec)
|
||||
{
|
||||
std::string reason = "";
|
||||
std::string reason;
|
||||
ec = lib::error_code();
|
||||
|
||||
if (payload.size() > 2) {
|
||||
|
||||
@@ -101,9 +101,19 @@ namespace lib {
|
||||
bool is_neg(T duration) {
|
||||
return duration.count() < 0;
|
||||
}
|
||||
inline lib::chrono::milliseconds milliseconds(long duration) {
|
||||
return lib::chrono::milliseconds(duration);
|
||||
}
|
||||
|
||||
// If boost believes it has std::chrono available it will use it
|
||||
// so we should also use it for things that relate to boost, even
|
||||
// if the library would otherwise use boost::chrono.
|
||||
#if defined(BOOST_ASIO_HAS_STD_CHRONO)
|
||||
inline std::chrono::milliseconds milliseconds(long duration) {
|
||||
return std::chrono::milliseconds(duration);
|
||||
}
|
||||
#else
|
||||
inline lib::chrono::milliseconds milliseconds(long duration) {
|
||||
return lib::chrono::milliseconds(duration);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
// Using boost::asio <1.49 we pretend a deadline timer is a steady
|
||||
// timer and wrap the negative detection and duration conversion
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
#ifndef WEBSOCKETPP_COMMON_ASIO_SSL_HPP
|
||||
#define WEBSOCKETPP_COMMON_ASIO_SSL_HPP
|
||||
|
||||
// NOTE: This file must be included before common/asio.hpp
|
||||
|
||||
#ifdef ASIO_STANDALONE
|
||||
#include <asio/asio/ssl.hpp>
|
||||
#include <asio/ssl.hpp>
|
||||
#else
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#endif
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace lib {
|
||||
using std::enable_shared_from_this;
|
||||
using std::static_pointer_cast;
|
||||
using std::make_shared;
|
||||
using std::unique_ptr;
|
||||
|
||||
typedef std::unique_ptr<unsigned char[]> unique_ptr_uchar_array;
|
||||
#else
|
||||
|
||||
@@ -43,6 +43,14 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If we're on Visual Studio 2013 or higher and haven't explicitly disabled
|
||||
// the use of C++11 thread header then prefer it to boost.
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1800 && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
|
||||
#ifndef _WEBSOCKETPP_CPP11_THREAD_
|
||||
#define _WEBSOCKETPP_CPP11_THREAD_
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WEBSOCKETPP_CPP11_THREAD_
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
@@ -53,8 +53,10 @@ namespace lib {
|
||||
|
||||
#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_
|
||||
using std::aligned_storage;
|
||||
using std::is_same;
|
||||
#else
|
||||
using boost::aligned_storage;
|
||||
using boost::is_same;
|
||||
#endif
|
||||
|
||||
} // namespace lib
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
|
||||
// Loggers
|
||||
#include <websocketpp/logger/basic.hpp>
|
||||
#include <websocketpp/logger/levels.hpp>
|
||||
|
||||
// RNG
|
||||
#include <websocketpp/random/none.hpp>
|
||||
@@ -188,7 +189,18 @@ struct core {
|
||||
static const websocketpp::log::level alog_level =
|
||||
websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel;
|
||||
|
||||
///
|
||||
/// Size of the per-connection read buffer
|
||||
/**
|
||||
* Each connection has an internal buffer of this size. A larger value will
|
||||
* result in fewer trips through the library and less CPU overhead at the
|
||||
* expense of increased memory usage per connection.
|
||||
*
|
||||
* If your application primarily deals in very large messages you may want
|
||||
* to try setting this value higher.
|
||||
*
|
||||
* If your application has a lot of connections or primarily deals in small
|
||||
* messages you may want to try setting this smaller.
|
||||
*/
|
||||
static const size_t connection_read_buffer_size = 16384;
|
||||
|
||||
/// Drop connections immediately on protocol error.
|
||||
|
||||
+85
-30
@@ -294,8 +294,8 @@ private:
|
||||
};
|
||||
public:
|
||||
|
||||
explicit connection(bool p_is_server, std::string const & ua, alog_type& alog,
|
||||
elog_type& elog, rng_type & rng)
|
||||
explicit connection(bool p_is_server, std::string const & ua, const lib::shared_ptr<alog_type>& alog,
|
||||
const lib::shared_ptr<elog_type>& elog, rng_type & rng)
|
||||
: transport_con_type(p_is_server, alog, elog)
|
||||
, m_handle_read_frame(lib::bind(
|
||||
&type::handle_read_frame,
|
||||
@@ -329,7 +329,7 @@ public:
|
||||
, m_http_state(session::http_state::init)
|
||||
, m_was_clean(false)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"connection constructor");
|
||||
m_alog->write(log::alevel::devel,"connection constructor");
|
||||
}
|
||||
|
||||
/// Get a shared pointer to this component
|
||||
@@ -628,7 +628,10 @@ public:
|
||||
*/
|
||||
size_t get_buffered_amount() const;
|
||||
|
||||
/// DEPRECATED: use get_buffered_amount instead
|
||||
/// Get the size of the outgoing write buffer (in payload bytes)
|
||||
/**
|
||||
* @deprecated use `get_buffered_amount` instead
|
||||
*/
|
||||
size_t buffered_amount() const {
|
||||
return get_buffered_amount();
|
||||
}
|
||||
@@ -969,6 +972,30 @@ public:
|
||||
*/
|
||||
std::string const & get_response_header(std::string const & key) const;
|
||||
|
||||
/// Get response HTTP status code
|
||||
/**
|
||||
* Gets the response status code
|
||||
*
|
||||
* @since 0.7.0
|
||||
*
|
||||
* @return The response status code sent
|
||||
*/
|
||||
http::status_code::value get_response_code() const {
|
||||
return m_response.get_status_code();
|
||||
}
|
||||
|
||||
/// Get response HTTP status message
|
||||
/**
|
||||
* Gets the response status message
|
||||
*
|
||||
* @since 0.7.0
|
||||
*
|
||||
* @return The response status message sent
|
||||
*/
|
||||
std::string const & get_response_msg() const {
|
||||
return m_response.get_status_msg();
|
||||
}
|
||||
|
||||
/// Set response status code and message
|
||||
/**
|
||||
* Sets the response status code to `code` and looks up the corresponding
|
||||
@@ -1076,6 +1103,25 @@ public:
|
||||
return m_request;
|
||||
}
|
||||
|
||||
/// Get response object
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.7.0
|
||||
*
|
||||
* @return A const reference to the raw response object
|
||||
*/
|
||||
response_type const & get_response() const {
|
||||
return m_response;
|
||||
}
|
||||
|
||||
/// Defer HTTP Response until later (Exception free)
|
||||
/**
|
||||
* Used in the http handler to defer the HTTP response for this connection
|
||||
@@ -1209,26 +1255,6 @@ public:
|
||||
return m_ec;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// The remaining public member functions are for internal/policy use //
|
||||
// only. Do not call from application code unless you understand what //
|
||||
// you are doing. //
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Set Connection Handle
|
||||
/**
|
||||
* The connection handle is a token that can be shared outside the
|
||||
* WebSocket++ core for the purposes of identifying a connection and
|
||||
* sending it messages.
|
||||
*
|
||||
* @param hdl A connection_hdl that the connection will use to refer
|
||||
* to itself.
|
||||
*/
|
||||
void set_handle(connection_hdl hdl) {
|
||||
m_connection_hdl = hdl;
|
||||
transport_con_type::set_handle(hdl);
|
||||
}
|
||||
|
||||
/// Get a message buffer
|
||||
/**
|
||||
* Warning: The API related to directly sending message buffers may change
|
||||
@@ -1254,7 +1280,13 @@ public:
|
||||
return m_msg_manager->get_message(op, size);
|
||||
}
|
||||
|
||||
void start();
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// The remaining public member functions are for internal/policy use //
|
||||
// only. Do not call from application code unless you understand what //
|
||||
// you are doing. //
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
void read_handshake(size_t num_bytes);
|
||||
|
||||
@@ -1304,6 +1336,27 @@ public:
|
||||
* non-zero otherwise.
|
||||
*/
|
||||
void handle_write_frame(lib::error_code const & ec);
|
||||
// protected:
|
||||
// This set of methods would really like to be protected, but doing so
|
||||
// requires that the endpoint be able to friend the connection. This is
|
||||
// allowed with C++11, but not prior versions
|
||||
|
||||
/// Start the connection state machine
|
||||
void start();
|
||||
|
||||
/// Set Connection Handle
|
||||
/**
|
||||
* The connection handle is a token that can be shared outside the
|
||||
* WebSocket++ core for the purposes of identifying a connection and
|
||||
* sending it messages.
|
||||
*
|
||||
* @param hdl A connection_hdl that the connection will use to refer
|
||||
* to itself.
|
||||
*/
|
||||
void set_handle(connection_hdl hdl) {
|
||||
m_connection_hdl = hdl;
|
||||
transport_con_type::set_handle(hdl);
|
||||
}
|
||||
protected:
|
||||
void handle_transport_init(lib::error_code const & ec);
|
||||
|
||||
@@ -1315,6 +1368,8 @@ protected:
|
||||
/// set m_response and return an error code indicating status.
|
||||
lib::error_code process_handshake_request();
|
||||
private:
|
||||
|
||||
|
||||
/// Completes m_response, serializes it, and sends it out on the wire.
|
||||
void write_http_response(lib::error_code const & ec);
|
||||
|
||||
@@ -1342,7 +1397,7 @@ private:
|
||||
* @return A status code, zero on success, non-zero otherwise
|
||||
*/
|
||||
lib::error_code send_close_ack(close::status::value code =
|
||||
close::status::blank, std::string const & reason = "");
|
||||
close::status::blank, std::string const & reason = std::string());
|
||||
|
||||
/// Send close frame
|
||||
/**
|
||||
@@ -1360,7 +1415,7 @@ private:
|
||||
* @return A status code, zero on success, non-zero otherwise
|
||||
*/
|
||||
lib::error_code send_close_frame(close::status::value code =
|
||||
close::status::blank, std::string const & reason = "", bool ack = false,
|
||||
close::status::blank, std::string const & reason = std::string(), bool ack = false,
|
||||
bool terminal = false);
|
||||
|
||||
/// Get a pointer to a new WebSocket protocol processor for a given version
|
||||
@@ -1431,7 +1486,7 @@ private:
|
||||
void log_err(log::level l, char const * msg, error_type const & ec) {
|
||||
std::stringstream s;
|
||||
s << msg << " error: " << ec << " (" << ec.message() << ")";
|
||||
m_elog.write(l, s.str());
|
||||
m_elog->write(l, s.str());
|
||||
}
|
||||
|
||||
// internal handler functions
|
||||
@@ -1548,8 +1603,8 @@ private:
|
||||
std::vector<std::string> m_requested_subprotocols;
|
||||
|
||||
bool const m_is_server;
|
||||
alog_type& m_alog;
|
||||
elog_type& m_elog;
|
||||
const lib::shared_ptr<alog_type> m_alog;
|
||||
const lib::shared_ptr<elog_type> m_elog;
|
||||
|
||||
rng_type & m_rng;
|
||||
|
||||
|
||||
+27
-24
@@ -85,9 +85,12 @@ public:
|
||||
// TODO: organize these
|
||||
typedef typename connection_type::termination_handler termination_handler;
|
||||
|
||||
// This would be ideal. Requires C++11 though
|
||||
//friend connection;
|
||||
|
||||
explicit endpoint(bool p_is_server)
|
||||
: m_alog(config::alog_level, log::channel_type_hint::access)
|
||||
, m_elog(config::elog_level, log::channel_type_hint::error)
|
||||
: 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_user_agent(::websocketpp::user_agent)
|
||||
, m_open_handshake_timeout_dur(config::timeout_open_handshake)
|
||||
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
|
||||
@@ -96,12 +99,12 @@ public:
|
||||
, m_max_http_body_size(config::max_http_body_size)
|
||||
, m_is_server(p_is_server)
|
||||
{
|
||||
m_alog.set_channels(config::alog_level);
|
||||
m_elog.set_channels(config::elog_level);
|
||||
m_alog->set_channels(config::alog_level);
|
||||
m_elog->set_channels(config::elog_level);
|
||||
|
||||
m_alog.write(log::alevel::devel, "endpoint constructor");
|
||||
m_alog->write(log::alevel::devel, "endpoint constructor");
|
||||
|
||||
transport_type::init_logging(&m_alog, &m_elog);
|
||||
transport_type::init_logging(m_alog, m_elog);
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +218,7 @@ public:
|
||||
* @param channels The channel value(s) to set
|
||||
*/
|
||||
void set_access_channels(log::level channels) {
|
||||
m_alog.set_channels(channels);
|
||||
m_alog->set_channels(channels);
|
||||
}
|
||||
|
||||
/// Clear Access logging channels
|
||||
@@ -226,7 +229,7 @@ public:
|
||||
* @param channels The channel value(s) to clear
|
||||
*/
|
||||
void clear_access_channels(log::level channels) {
|
||||
m_alog.clear_channels(channels);
|
||||
m_alog->clear_channels(channels);
|
||||
}
|
||||
|
||||
/// Set Error logging channel
|
||||
@@ -237,7 +240,7 @@ public:
|
||||
* @param channels The channel value(s) to set
|
||||
*/
|
||||
void set_error_channels(log::level channels) {
|
||||
m_elog.set_channels(channels);
|
||||
m_elog->set_channels(channels);
|
||||
}
|
||||
|
||||
/// Clear Error logging channels
|
||||
@@ -248,7 +251,7 @@ public:
|
||||
* @param channels The channel value(s) to clear
|
||||
*/
|
||||
void clear_error_channels(log::level channels) {
|
||||
m_elog.clear_channels(channels);
|
||||
m_elog->clear_channels(channels);
|
||||
}
|
||||
|
||||
/// Get reference to access logger
|
||||
@@ -256,7 +259,7 @@ public:
|
||||
* @return A reference to the access logger
|
||||
*/
|
||||
alog_type & get_alog() {
|
||||
return m_alog;
|
||||
return *m_alog;
|
||||
}
|
||||
|
||||
/// Get reference to error logger
|
||||
@@ -264,7 +267,7 @@ public:
|
||||
* @return A reference to the error logger
|
||||
*/
|
||||
elog_type & get_elog() {
|
||||
return m_elog;
|
||||
return *m_elog;
|
||||
}
|
||||
|
||||
/*************************/
|
||||
@@ -272,52 +275,52 @@ public:
|
||||
/*************************/
|
||||
|
||||
void set_open_handler(open_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_open_handler");
|
||||
m_alog->write(log::alevel::devel,"set_open_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_open_handler = h;
|
||||
}
|
||||
void set_close_handler(close_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_close_handler");
|
||||
m_alog->write(log::alevel::devel,"set_close_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_close_handler = h;
|
||||
}
|
||||
void set_fail_handler(fail_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_fail_handler");
|
||||
m_alog->write(log::alevel::devel,"set_fail_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_fail_handler = h;
|
||||
}
|
||||
void set_ping_handler(ping_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_ping_handler");
|
||||
m_alog->write(log::alevel::devel,"set_ping_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_ping_handler = h;
|
||||
}
|
||||
void set_pong_handler(pong_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_pong_handler");
|
||||
m_alog->write(log::alevel::devel,"set_pong_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_pong_handler = h;
|
||||
}
|
||||
void set_pong_timeout_handler(pong_timeout_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_pong_timeout_handler");
|
||||
m_alog->write(log::alevel::devel,"set_pong_timeout_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_pong_timeout_handler = h;
|
||||
}
|
||||
void set_interrupt_handler(interrupt_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_interrupt_handler");
|
||||
m_alog->write(log::alevel::devel,"set_interrupt_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_interrupt_handler = h;
|
||||
}
|
||||
void set_http_handler(http_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_http_handler");
|
||||
m_alog->write(log::alevel::devel,"set_http_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_http_handler = h;
|
||||
}
|
||||
void set_validate_handler(validate_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_validate_handler");
|
||||
m_alog->write(log::alevel::devel,"set_validate_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_validate_handler = h;
|
||||
}
|
||||
void set_message_handler(message_handler h) {
|
||||
m_alog.write(log::alevel::devel,"set_message_handler");
|
||||
m_alog->write(log::alevel::devel,"set_message_handler");
|
||||
scoped_lock_type guard(m_mutex);
|
||||
m_message_handler = h;
|
||||
}
|
||||
@@ -658,8 +661,8 @@ public:
|
||||
protected:
|
||||
connection_ptr create_connection();
|
||||
|
||||
alog_type m_alog;
|
||||
elog_type m_elog;
|
||||
lib::shared_ptr<alog_type> m_alog;
|
||||
lib::shared_ptr<elog_type> m_elog;
|
||||
private:
|
||||
// dynamic settings
|
||||
std::string m_user_agent;
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace http {
|
||||
static char const header_separator[] = ":";
|
||||
|
||||
/// Literal value of an empty header
|
||||
static std::string const empty_header = "";
|
||||
static std::string const empty_header;
|
||||
|
||||
/// Maximum size in bytes before rejecting an HTTP header as too big.
|
||||
size_t const max_header_size = 16000;
|
||||
@@ -284,8 +284,8 @@ namespace http {
|
||||
public:
|
||||
exception(const std::string& log_msg,
|
||||
status_code::value error_code,
|
||||
const std::string& error_msg = "",
|
||||
const std::string& body = "")
|
||||
const std::string& error_msg = std::string(),
|
||||
const std::string& body = std::string())
|
||||
: m_msg(log_msg)
|
||||
, m_error_msg(error_msg)
|
||||
, m_body(body)
|
||||
|
||||
@@ -71,7 +71,7 @@ inline void parser::append_header(std::string const & key, std::string const &
|
||||
throw exception("Invalid header name",status_code::bad_request);
|
||||
}
|
||||
|
||||
if (this->get_header(key) == "") {
|
||||
if (this->get_header(key).empty()) {
|
||||
m_headers[key] = val;
|
||||
} else {
|
||||
m_headers[key] += ", " + val;
|
||||
@@ -91,7 +91,7 @@ inline void parser::remove_header(std::string const & key) {
|
||||
inline void parser::set_body(std::string const & value) {
|
||||
if (value.size() == 0) {
|
||||
remove_header("Content-Length");
|
||||
m_body = "";
|
||||
m_body.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ inline bool parser::parse_parameter_list(std::string const & in,
|
||||
}
|
||||
|
||||
inline bool parser::prepare_body() {
|
||||
if (get_header("Content-Length") != "") {
|
||||
if (!get_header("Content-Length").empty()) {
|
||||
std::string const & cl_header = get_header("Content-Length");
|
||||
char * end;
|
||||
|
||||
@@ -176,6 +176,10 @@ inline void parser::process_header(std::string::iterator begin,
|
||||
strip_lws(std::string(cursor+sizeof(header_separator)-1,end)));
|
||||
}
|
||||
|
||||
inline header_list const & parser::get_headers() const {
|
||||
return m_headers;
|
||||
}
|
||||
|
||||
inline std::string parser::raw_headers() const {
|
||||
std::stringstream raw;
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ inline size_t request::consume(char const * buf, size_t len) {
|
||||
//the range [begin,end) now represents a line to be processed.
|
||||
if (end-begin == 0) {
|
||||
// we got a blank line
|
||||
if (m_method.empty() || get_header("Host") == "") {
|
||||
if (m_method.empty() || get_header("Host").empty()) {
|
||||
throw exception("Incomplete Request",status_code::bad_request);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ inline size_t response::consume(char const * buf, size_t len) {
|
||||
// TODO: grab content-length
|
||||
std::string length = get_header("Content-Length");
|
||||
|
||||
if (length == "") {
|
||||
if (length.empty()) {
|
||||
// no content length found, read indefinitely
|
||||
m_read = 0;
|
||||
} else {
|
||||
|
||||
@@ -230,7 +230,7 @@ InputIterator extract_attributes(InputIterator begin, InputIterator end,
|
||||
cursor = http::parser::extract_all_lws(cursor,end);
|
||||
ret = http::parser::extract_token(cursor,end);
|
||||
|
||||
if (ret.first == "") {
|
||||
if (ret.first.empty()) {
|
||||
// error: expected a token
|
||||
return begin;
|
||||
} else {
|
||||
@@ -242,7 +242,7 @@ InputIterator extract_attributes(InputIterator begin, InputIterator end,
|
||||
if (cursor == end || *cursor != '=') {
|
||||
// if there is an equals sign, read the attribute value. Otherwise
|
||||
// record a blank value and continue
|
||||
attributes[name] = "";
|
||||
attributes[name].clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ InputIterator extract_attributes(InputIterator begin, InputIterator end,
|
||||
}
|
||||
|
||||
ret = http::parser::extract_token(cursor,end);
|
||||
if (ret.first == "") {
|
||||
if (ret.first.empty()) {
|
||||
// error : expected token or quoted string
|
||||
return begin;
|
||||
} else {
|
||||
@@ -321,7 +321,7 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end,
|
||||
|
||||
ret = http::parser::extract_token(cursor,end);
|
||||
|
||||
if (ret.first == "") {
|
||||
if (ret.first.empty()) {
|
||||
// error: expected a token
|
||||
return begin;
|
||||
} else {
|
||||
@@ -441,6 +441,16 @@ public:
|
||||
bool get_header_as_plist(std::string const & key, parameter_list & out)
|
||||
const;
|
||||
|
||||
/// Return a list of all HTTP headers
|
||||
/**
|
||||
* Return a list of all HTTP headers
|
||||
*
|
||||
* @since 0.8.0
|
||||
*
|
||||
* @return A list of all HTTP headers
|
||||
*/
|
||||
header_list const & get_headers() const;
|
||||
|
||||
/// Append a value to an existing HTTP header
|
||||
/**
|
||||
* This method will set the value of the HTTP header `key` with the
|
||||
|
||||
@@ -53,7 +53,7 @@ template <typename config>
|
||||
void connection<config>::set_termination_handler(
|
||||
termination_handler new_handler)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"connection set_termination_handler");
|
||||
|
||||
//scoped_lock_type lock(m_connection_state_lock);
|
||||
@@ -103,8 +103,8 @@ lib::error_code connection<config>::send(void const * payload, size_t len,
|
||||
template <typename config>
|
||||
lib::error_code connection<config>::send(typename config::message_type::ptr msg)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"connection send");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"connection send");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -153,8 +153,8 @@ lib::error_code connection<config>::send(typename config::message_type::ptr msg)
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::ping(std::string const& payload, lib::error_code& ec) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"connection ping");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"connection ping");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -162,7 +162,7 @@ void connection<config>::ping(std::string const& payload, lib::error_code& ec) {
|
||||
if (m_state != session::state::open) {
|
||||
std::stringstream ss;
|
||||
ss << "connection::ping called from invalid state " << m_state;
|
||||
m_alog.write(log::alevel::devel,ss.str());
|
||||
m_alog->write(log::alevel::devel,ss.str());
|
||||
ec = error::make_error_code(error::invalid_state);
|
||||
return;
|
||||
}
|
||||
@@ -198,7 +198,7 @@ void connection<config>::ping(std::string const& payload, lib::error_code& ec) {
|
||||
|
||||
if (!m_ping_timer) {
|
||||
// Our transport doesn't support timers
|
||||
m_elog.write(log::elevel::warn,"Warning: a pong_timeout_handler is \
|
||||
m_elog->write(log::elevel::warn,"Warning: a pong_timeout_handler is \
|
||||
set but the transport in use does not support timeouts.");
|
||||
}
|
||||
}
|
||||
@@ -239,7 +239,7 @@ void connection<config>::handle_pong_timeout(std::string payload,
|
||||
return;
|
||||
}
|
||||
|
||||
m_elog.write(log::elevel::devel,"pong_timeout error: "+ec.message());
|
||||
m_elog->write(log::elevel::devel,"pong_timeout error: "+ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,8 +250,8 @@ void connection<config>::handle_pong_timeout(std::string payload,
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::pong(std::string const& payload, lib::error_code& ec) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"connection pong");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"connection pong");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -259,7 +259,7 @@ void connection<config>::pong(std::string const& payload, lib::error_code& ec) {
|
||||
if (m_state != session::state::open) {
|
||||
std::stringstream ss;
|
||||
ss << "connection::pong called from invalid state " << m_state;
|
||||
m_alog.write(log::alevel::devel,ss.str());
|
||||
m_alog->write(log::alevel::devel,ss.str());
|
||||
ec = error::make_error_code(error::invalid_state);
|
||||
return;
|
||||
}
|
||||
@@ -304,8 +304,8 @@ template <typename config>
|
||||
void connection<config>::close(close::status::value const code,
|
||||
std::string const & reason, lib::error_code & ec)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"connection close");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"connection close");
|
||||
}
|
||||
|
||||
// Truncate reason to maximum size allowable in a close frame.
|
||||
@@ -339,7 +339,7 @@ void connection<config>::close(close::status::value const code,
|
||||
*/
|
||||
template <typename config>
|
||||
lib::error_code connection<config>::interrupt() {
|
||||
m_alog.write(log::alevel::devel,"connection connection::interrupt");
|
||||
m_alog->write(log::alevel::devel,"connection connection::interrupt");
|
||||
return transport_con_type::interrupt(
|
||||
lib::bind(
|
||||
&type::handle_interrupt,
|
||||
@@ -358,7 +358,7 @@ void connection<config>::handle_interrupt() {
|
||||
|
||||
template <typename config>
|
||||
lib::error_code connection<config>::pause_reading() {
|
||||
m_alog.write(log::alevel::devel,"connection connection::pause_reading");
|
||||
m_alog->write(log::alevel::devel,"connection connection::pause_reading");
|
||||
return transport_con_type::dispatch(
|
||||
lib::bind(
|
||||
&type::handle_pause_reading,
|
||||
@@ -370,13 +370,13 @@ lib::error_code connection<config>::pause_reading() {
|
||||
/// Pause reading handler. Not safe to call directly
|
||||
template <typename config>
|
||||
void connection<config>::handle_pause_reading() {
|
||||
m_alog.write(log::alevel::devel,"connection connection::handle_pause_reading");
|
||||
m_alog->write(log::alevel::devel,"connection connection::handle_pause_reading");
|
||||
m_read_flag = false;
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
lib::error_code connection<config>::resume_reading() {
|
||||
m_alog.write(log::alevel::devel,"connection connection::resume_reading");
|
||||
m_alog->write(log::alevel::devel,"connection connection::resume_reading");
|
||||
return transport_con_type::dispatch(
|
||||
lib::bind(
|
||||
&type::handle_resume_reading,
|
||||
@@ -714,10 +714,10 @@ void connection<config>::send_http_response() {
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::start() {
|
||||
m_alog.write(log::alevel::devel,"connection start");
|
||||
m_alog->write(log::alevel::devel,"connection start");
|
||||
|
||||
if (m_internal_state != istate::USER_INIT) {
|
||||
m_alog.write(log::alevel::devel,"Start called in invalid state");
|
||||
m_alog->write(log::alevel::devel,"Start called in invalid state");
|
||||
this->terminate(error::make_error_code(error::invalid_state));
|
||||
return;
|
||||
}
|
||||
@@ -738,12 +738,12 @@ void connection<config>::start() {
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::handle_transport_init(lib::error_code const & ec) {
|
||||
m_alog.write(log::alevel::devel,"connection handle_transport_init");
|
||||
m_alog->write(log::alevel::devel,"connection handle_transport_init");
|
||||
|
||||
lib::error_code ecm = ec;
|
||||
|
||||
if (m_internal_state != istate::TRANSPORT_INIT) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_transport_init must be called from transport init state");
|
||||
ecm = error::make_error_code(error::invalid_state);
|
||||
}
|
||||
@@ -751,7 +751,7 @@ void connection<config>::handle_transport_init(lib::error_code const & ec) {
|
||||
if (ecm) {
|
||||
std::stringstream s;
|
||||
s << "handle_transport_init received error: "<< ecm.message();
|
||||
m_elog.write(log::elevel::rerror,s.str());
|
||||
m_elog->write(log::elevel::rerror,s.str());
|
||||
|
||||
this->terminate(ecm);
|
||||
return;
|
||||
@@ -772,7 +772,7 @@ void connection<config>::handle_transport_init(lib::error_code const & ec) {
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::read_handshake(size_t num_bytes) {
|
||||
m_alog.write(log::alevel::devel,"connection read");
|
||||
m_alog->write(log::alevel::devel,"connection read_handshake");
|
||||
|
||||
if (m_open_handshake_timeout_dur > 0) {
|
||||
m_handshake_timer = transport_con_type::set_timer(
|
||||
@@ -804,7 +804,7 @@ template <typename config>
|
||||
void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"connection handle_read_handshake");
|
||||
m_alog->write(log::alevel::devel,"connection handle_read_handshake");
|
||||
|
||||
lib::error_code ecm = ec;
|
||||
|
||||
@@ -819,7 +819,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
// The connection was canceled while the response was being sent,
|
||||
// usually by the handshake timer. This is basically expected
|
||||
// (though hopefully rare) and there is nothing we can do so ignore.
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_read_handshake invoked after connection was closed");
|
||||
return;
|
||||
} else {
|
||||
@@ -830,7 +830,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
if (ecm) {
|
||||
if (ecm == transport::error::eof && m_state == session::state::closed) {
|
||||
// we expect to get eof if the connection is closed already
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"got (expected) eof/state error from closed con");
|
||||
return;
|
||||
}
|
||||
@@ -842,7 +842,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
|
||||
// Boundaries checking. TODO: How much of this should be done?
|
||||
if (bytes_transferred > config::connection_read_buffer_size) {
|
||||
m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
|
||||
m_elog->write(log::elevel::fatal,"Fatal boundaries checking error.");
|
||||
this->terminate(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
@@ -861,16 +861,16 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
// More paranoid boundaries checking.
|
||||
// TODO: Is this overkill?
|
||||
if (bytes_processed > bytes_transferred) {
|
||||
m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
|
||||
m_elog->write(log::elevel::fatal,"Fatal boundaries checking error.");
|
||||
this->terminate(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "bytes_transferred: " << bytes_transferred
|
||||
<< " bytes, bytes processed: " << bytes_processed << " bytes";
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
if (m_request.ready()) {
|
||||
@@ -891,17 +891,17 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
bytes_processed += 8;
|
||||
} else {
|
||||
// TODO: need more bytes
|
||||
m_alog.write(log::alevel::devel,"short key3 read");
|
||||
m_alog->write(log::alevel::devel,"short key3 read");
|
||||
m_response.set_status(http::status_code::internal_server_error);
|
||||
this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,m_request.raw());
|
||||
if (m_request.get_header("Sec-WebSocket-Key3") != "") {
|
||||
m_alog.write(log::alevel::devel,
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,m_request.raw());
|
||||
if (!m_request.get_header("Sec-WebSocket-Key3").empty()) {
|
||||
m_alog->write(log::alevel::devel,
|
||||
utility::to_hex(m_request.get_header("Sec-WebSocket-Key3")));
|
||||
}
|
||||
}
|
||||
@@ -917,7 +917,11 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
|
||||
// We have the complete request. Process it.
|
||||
lib::error_code handshake_ec = this->process_handshake_request();
|
||||
if (!m_is_http || m_http_state != session::http_state::deferred) {
|
||||
|
||||
// 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) {
|
||||
this->write_http_response(handshake_ec);
|
||||
}
|
||||
} else {
|
||||
@@ -944,7 +948,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
template <typename config>
|
||||
void connection<config>::write_http_response_error(lib::error_code const & ec) {
|
||||
if (m_internal_state != istate::READ_HTTP_REQUEST) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"write_http_response_error called in invalid state");
|
||||
this->terminate(error::make_error_code(error::invalid_state));
|
||||
return;
|
||||
@@ -961,7 +965,7 @@ template <typename config>
|
||||
void connection<config>::handle_read_frame(lib::error_code const & ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
//m_alog.write(log::alevel::devel,"connection handle_read_frame");
|
||||
//m_alog->write(log::alevel::devel,"connection handle_read_frame");
|
||||
|
||||
lib::error_code ecm = ec;
|
||||
|
||||
@@ -976,7 +980,7 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
|
||||
if (m_state == session::state::closed) {
|
||||
// we expect to get eof if the connection is closed already
|
||||
// just ignore it
|
||||
m_alog.write(log::alevel::devel,"got eof from closed con");
|
||||
m_alog->write(log::alevel::devel,"got eof from closed con");
|
||||
return;
|
||||
} else if (m_state == session::state::closing && !m_is_server) {
|
||||
// If we are a client we expect to get eof in the closing state,
|
||||
@@ -991,7 +995,7 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
|
||||
// changed and should be ignored as they pose no problems and there
|
||||
// is nothing useful that we can do about them.
|
||||
if (m_state == session::state::closed) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_read_frame: got invalid istate in closed state");
|
||||
return;
|
||||
}
|
||||
@@ -1015,32 +1019,32 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
|
||||
|
||||
// Boundaries checking. TODO: How much of this should be done?
|
||||
/*if (bytes_transferred > config::connection_read_buffer_size) {
|
||||
m_elog.write(log::elevel::fatal,"Fatal boundaries checking error");
|
||||
m_elog->write(log::elevel::fatal,"Fatal boundaries checking error");
|
||||
this->terminate(make_error_code(error::general));
|
||||
return;
|
||||
}*/
|
||||
|
||||
size_t p = 0;
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "p = " << p << " bytes transferred = " << bytes_transferred;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
while (p < bytes_transferred) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "calling consume with " << bytes_transferred-p << " bytes";
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
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<uint8_t*>(m_buf)+p,bytes_transferred-p);
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
p += m_processor->consume(
|
||||
@@ -1049,10 +1053,10 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
|
||||
consume_ec
|
||||
);
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "bytes left after consume: " << bytes_transferred-p;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
if (consume_ec) {
|
||||
log_err(log::elevel::rerror, "consume", consume_ec);
|
||||
@@ -1078,20 +1082,20 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
|
||||
}
|
||||
|
||||
if (m_processor->ready()) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "Complete message received. Dispatching";
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
message_ptr msg = m_processor->get_message();
|
||||
|
||||
if (!msg) {
|
||||
m_alog.write(log::alevel::devel, "null message from m_processor");
|
||||
m_alog->write(log::alevel::devel, "null message from m_processor");
|
||||
} else if (!is_control(msg->get_opcode())) {
|
||||
// data message, dispatch to user
|
||||
if (m_state != session::state::open) {
|
||||
m_elog.write(log::elevel::warn, "got non-close frame while closing");
|
||||
m_elog->write(log::elevel::warn, "got non-close frame while closing");
|
||||
} else if (m_message_handler) {
|
||||
m_message_handler(m_connection_hdl, msg);
|
||||
}
|
||||
@@ -1128,7 +1132,7 @@ void connection<config>::read_frame() {
|
||||
|
||||
template <typename config>
|
||||
lib::error_code connection<config>::initialize_processor() {
|
||||
m_alog.write(log::alevel::devel,"initialize_processor");
|
||||
m_alog->write(log::alevel::devel,"initialize_processor");
|
||||
|
||||
// if it isn't a websocket handshake nothing to do.
|
||||
if (!processor::is_websocket_handshake(m_request)) {
|
||||
@@ -1138,7 +1142,7 @@ lib::error_code connection<config>::initialize_processor() {
|
||||
int version = processor::get_websocket_version(m_request);
|
||||
|
||||
if (version < 0) {
|
||||
m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
|
||||
m_alog->write(log::alevel::devel, "BAD REQUEST: can't determine version");
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return error::make_error_code(error::invalid_version);
|
||||
}
|
||||
@@ -1152,11 +1156,11 @@ lib::error_code connection<config>::initialize_processor() {
|
||||
|
||||
// We don't have a processor for this version. Return bad request
|
||||
// with Sec-WebSocket-Version header filled with values we do accept
|
||||
m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version");
|
||||
m_alog->write(log::alevel::devel, "BAD REQUEST: no processor for version");
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
|
||||
std::stringstream ss;
|
||||
std::string sep = "";
|
||||
std::string sep;
|
||||
std::vector<int>::const_iterator it;
|
||||
for (it = versions_supported.begin(); it != versions_supported.end(); it++)
|
||||
{
|
||||
@@ -1170,11 +1174,11 @@ lib::error_code connection<config>::initialize_processor() {
|
||||
|
||||
template <typename config>
|
||||
lib::error_code connection<config>::process_handshake_request() {
|
||||
m_alog.write(log::alevel::devel,"process handshake request");
|
||||
m_alog->write(log::alevel::devel,"process handshake request");
|
||||
|
||||
if (!processor::is_websocket_handshake(m_request)) {
|
||||
// this is not a websocket handshake. Process as plain HTTP
|
||||
m_alog.write(log::alevel::devel,"HTTP REQUEST");
|
||||
m_alog->write(log::alevel::devel,"HTTP REQUEST");
|
||||
|
||||
// extract URI from request
|
||||
m_uri = processor::get_uri_from_host(
|
||||
@@ -1183,7 +1187,7 @@ lib::error_code connection<config>::process_handshake_request() {
|
||||
);
|
||||
|
||||
if (!m_uri->get_valid()) {
|
||||
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
|
||||
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);
|
||||
}
|
||||
@@ -1208,7 +1212,7 @@ lib::error_code connection<config>::process_handshake_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_alog->write(log::alevel::devel, "Bad request " + ec.message());
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return ec;
|
||||
}
|
||||
@@ -1221,7 +1225,7 @@ lib::error_code connection<config>::process_handshake_request() {
|
||||
if (neg_results.first) {
|
||||
// There was a fatal error in extension parsing that should result in
|
||||
// a failed connection attempt.
|
||||
m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
|
||||
m_alog->write(log::alevel::devel, "Bad request: " + neg_results.first.message());
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return neg_results.first;
|
||||
} else {
|
||||
@@ -1239,7 +1243,7 @@ lib::error_code connection<config>::process_handshake_request() {
|
||||
|
||||
|
||||
if (!m_uri->get_valid()) {
|
||||
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
|
||||
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);
|
||||
}
|
||||
@@ -1263,14 +1267,14 @@ lib::error_code connection<config>::process_handshake_request() {
|
||||
if (ec) {
|
||||
std::stringstream s;
|
||||
s << "Processing error: " << ec << "(" << ec.message() << ")";
|
||||
m_alog.write(log::alevel::devel, s.str());
|
||||
m_alog->write(log::alevel::devel, s.str());
|
||||
|
||||
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, "USER REJECT");
|
||||
|
||||
// Use Bad Request if the user handler did not provide a more
|
||||
// specific http response error code.
|
||||
@@ -1287,10 +1291,10 @@ lib::error_code connection<config>::process_handshake_request() {
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::write_http_response(lib::error_code const & ec) {
|
||||
m_alog.write(log::alevel::devel,"connection write_http_response");
|
||||
m_alog->write(log::alevel::devel,"connection write_http_response");
|
||||
|
||||
if (ec == error::make_error_code(error::http_connection_ended)) {
|
||||
m_alog.write(log::alevel::http,"An HTTP handler took over the connection.");
|
||||
m_alog->write(log::alevel::http,"An HTTP handler took over the connection.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1304,7 +1308,7 @@ void connection<config>::write_http_response(lib::error_code const & ec) {
|
||||
m_response.set_version("HTTP/1.1");
|
||||
|
||||
// Set server header based on the user agent settings
|
||||
if (m_response.get_header("Server") == "") {
|
||||
if (m_response.get_header("Server").empty()) {
|
||||
if (!m_user_agent.empty()) {
|
||||
m_response.replace_header("Server",m_user_agent);
|
||||
} else {
|
||||
@@ -1320,10 +1324,10 @@ void connection<config>::write_http_response(lib::error_code const & ec) {
|
||||
m_handshake_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);
|
||||
if (m_response.get_header("Sec-WebSocket-Key3") != "") {
|
||||
m_alog.write(log::alevel::devel,
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_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")));
|
||||
}
|
||||
}
|
||||
@@ -1342,7 +1346,7 @@ void connection<config>::write_http_response(lib::error_code const & ec) {
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::handle_write_http_response(lib::error_code const & ec) {
|
||||
m_alog.write(log::alevel::devel,"handle_write_http_response");
|
||||
m_alog->write(log::alevel::devel,"handle_write_http_response");
|
||||
|
||||
lib::error_code ecm = ec;
|
||||
|
||||
@@ -1357,7 +1361,7 @@ void connection<config>::handle_write_http_response(lib::error_code const & ec)
|
||||
// The connection was canceled while the response was being sent,
|
||||
// usually by the handshake timer. This is basically expected
|
||||
// (though hopefully rare) and there is nothing we can do so ignore.
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_write_http_response invoked after connection was closed");
|
||||
return;
|
||||
} else {
|
||||
@@ -1368,7 +1372,7 @@ void connection<config>::handle_write_http_response(lib::error_code const & ec)
|
||||
if (ecm) {
|
||||
if (ecm == transport::error::eof && m_state == session::state::closed) {
|
||||
// we expect to get eof if the connection is closed already
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"got (expected) eof/state error from closed con");
|
||||
return;
|
||||
}
|
||||
@@ -1393,7 +1397,7 @@ void connection<config>::handle_write_http_response(lib::error_code const & ec)
|
||||
std::stringstream s;
|
||||
s << "Handshake ended with HTTP error: "
|
||||
<< m_response.get_status_code();
|
||||
m_elog.write(log::elevel::rerror,s.str());
|
||||
m_elog->write(log::elevel::rerror,s.str());
|
||||
} else {
|
||||
// if this was not a websocket connection, we have written
|
||||
// the expected response and the connection can be closed.
|
||||
@@ -1401,7 +1405,7 @@ void connection<config>::handle_write_http_response(lib::error_code const & ec)
|
||||
this->log_http_result();
|
||||
|
||||
if (m_ec) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"got to writing HTTP results with m_ec set: "+m_ec.message());
|
||||
}
|
||||
m_ec = make_error_code(error::http_connection_ended);
|
||||
@@ -1425,7 +1429,7 @@ void connection<config>::handle_write_http_response(lib::error_code const & ec)
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::send_http_request() {
|
||||
m_alog.write(log::alevel::devel,"connection send_http_request");
|
||||
m_alog->write(log::alevel::devel,"connection send_http_request");
|
||||
|
||||
// TODO: origin header?
|
||||
|
||||
@@ -1441,12 +1445,12 @@ void connection<config>::send_http_request() {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
m_elog.write(log::elevel::fatal,"Internal library error: missing processor");
|
||||
m_elog->write(log::elevel::fatal,"Internal library error: missing processor");
|
||||
return;
|
||||
}
|
||||
|
||||
// Unless the user has overridden the user agent, send generic WS++ UA.
|
||||
if (m_request.get_header("User-Agent") == "") {
|
||||
if (m_request.get_header("User-Agent").empty()) {
|
||||
if (!m_user_agent.empty()) {
|
||||
m_request.replace_header("User-Agent",m_user_agent);
|
||||
} else {
|
||||
@@ -1456,8 +1460,8 @@ void connection<config>::send_http_request() {
|
||||
|
||||
m_handshake_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);
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
|
||||
}
|
||||
|
||||
if (m_open_handshake_timeout_dur > 0) {
|
||||
@@ -1484,7 +1488,7 @@ void connection<config>::send_http_request() {
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::handle_send_http_request(lib::error_code const & ec) {
|
||||
m_alog.write(log::alevel::devel,"handle_send_http_request");
|
||||
m_alog->write(log::alevel::devel,"handle_send_http_request");
|
||||
|
||||
lib::error_code ecm = ec;
|
||||
|
||||
@@ -1501,7 +1505,7 @@ void connection<config>::handle_send_http_request(lib::error_code const & ec) {
|
||||
// The connection was canceled while the response was being sent,
|
||||
// usually by the handshake timer. This is basically expected
|
||||
// (though hopefully rare) and there is nothing we can do so ignore.
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_send_http_request invoked after connection was closed");
|
||||
return;
|
||||
} else {
|
||||
@@ -1512,7 +1516,7 @@ void connection<config>::handle_send_http_request(lib::error_code const & ec) {
|
||||
if (ecm) {
|
||||
if (ecm == transport::error::eof && m_state == session::state::closed) {
|
||||
// we expect to get eof if the connection is closed already
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"got (expected) eof/state error from closed con");
|
||||
return;
|
||||
}
|
||||
@@ -1539,7 +1543,7 @@ template <typename config>
|
||||
void connection<config>::handle_read_http_response(lib::error_code const & ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"handle_read_http_response");
|
||||
m_alog->write(log::alevel::devel,"handle_read_http_response");
|
||||
|
||||
lib::error_code ecm = ec;
|
||||
|
||||
@@ -1554,7 +1558,7 @@ void connection<config>::handle_read_http_response(lib::error_code const & ec,
|
||||
// The connection was canceled while the response was being sent,
|
||||
// usually by the handshake timer. This is basically expected
|
||||
// (though hopefully rare) and there is nothing we can do so ignore.
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_read_http_response invoked after connection was closed");
|
||||
return;
|
||||
} else {
|
||||
@@ -1565,7 +1569,7 @@ void connection<config>::handle_read_http_response(lib::error_code const & ec,
|
||||
if (ecm) {
|
||||
if (ecm == transport::error::eof && m_state == session::state::closed) {
|
||||
// we expect to get eof if the connection is closed already
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"got (expected) eof/state error from closed con");
|
||||
return;
|
||||
}
|
||||
@@ -1580,13 +1584,13 @@ void connection<config>::handle_read_http_response(lib::error_code const & ec,
|
||||
try {
|
||||
bytes_processed = m_response.consume(m_buf,bytes_transferred);
|
||||
} catch (http::exception & e) {
|
||||
m_elog.write(log::elevel::rerror,
|
||||
m_elog->write(log::elevel::rerror,
|
||||
std::string("error in handle_read_http_response: ")+e.what());
|
||||
this->terminate(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
|
||||
m_alog.write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
|
||||
m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
|
||||
|
||||
if (m_response.headers_ready()) {
|
||||
if (m_handshake_timer) {
|
||||
@@ -1617,7 +1621,7 @@ void connection<config>::handle_read_http_response(lib::error_code const & ec,
|
||||
// doesn't match the options requested by the client. Its possible
|
||||
// that the best behavior in this cases is to log and continue with
|
||||
// an unextended connection.
|
||||
m_alog.write(log::alevel::devel, "Extension negotiation failed: "
|
||||
m_alog->write(log::alevel::devel, "Extension negotiation failed: "
|
||||
+ neg_results.first.message());
|
||||
this->terminate(make_error_code(error::extension_neg_failed));
|
||||
// TODO: close connection with reason 1010 (and list extensions)
|
||||
@@ -1660,13 +1664,13 @@ void connection<config>::handle_open_handshake_timeout(
|
||||
lib::error_code const & ec)
|
||||
{
|
||||
if (ec == transport::error::operation_aborted) {
|
||||
m_alog.write(log::alevel::devel,"open handshake timer cancelled");
|
||||
m_alog->write(log::alevel::devel,"open handshake timer cancelled");
|
||||
} else if (ec) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"open handle_open_handshake_timeout error: "+ec.message());
|
||||
// TODO: ignore or fail here?
|
||||
} else {
|
||||
m_alog.write(log::alevel::devel,"open handshake timer expired");
|
||||
m_alog->write(log::alevel::devel,"open handshake timer expired");
|
||||
terminate(make_error_code(error::open_handshake_timeout));
|
||||
}
|
||||
}
|
||||
@@ -1676,21 +1680,21 @@ void connection<config>::handle_close_handshake_timeout(
|
||||
lib::error_code const & ec)
|
||||
{
|
||||
if (ec == transport::error::operation_aborted) {
|
||||
m_alog.write(log::alevel::devel,"asio close handshake timer cancelled");
|
||||
m_alog->write(log::alevel::devel,"asio close handshake timer cancelled");
|
||||
} else if (ec) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio open handle_close_handshake_timeout error: "+ec.message());
|
||||
// TODO: ignore or fail here?
|
||||
} else {
|
||||
m_alog.write(log::alevel::devel, "asio close handshake timer expired");
|
||||
m_alog->write(log::alevel::devel, "asio close handshake timer expired");
|
||||
terminate(make_error_code(error::close_handshake_timeout));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::terminate(lib::error_code const & ec) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"connection terminate");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"connection terminate");
|
||||
}
|
||||
|
||||
// Cancel close handshake timer
|
||||
@@ -1723,7 +1727,7 @@ void connection<config>::terminate(lib::error_code const & ec) {
|
||||
m_state = session::state::closed;
|
||||
tstat = closed;
|
||||
} else {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"terminate called on connection that was already terminated");
|
||||
return;
|
||||
}
|
||||
@@ -1744,8 +1748,8 @@ template <typename config>
|
||||
void connection<config>::handle_terminate(terminate_status tstat,
|
||||
lib::error_code const & ec)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"connection handle_terminate");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"connection handle_terminate");
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
@@ -1766,7 +1770,7 @@ void connection<config>::handle_terminate(terminate_status tstat,
|
||||
}
|
||||
log_close_result();
|
||||
} else {
|
||||
m_elog.write(log::elevel::rerror,"Unknown terminate_status");
|
||||
m_elog->write(log::elevel::rerror,"Unknown terminate_status");
|
||||
}
|
||||
|
||||
// call the termination handler if it exists
|
||||
@@ -1776,7 +1780,7 @@ void connection<config>::handle_terminate(terminate_status tstat,
|
||||
try {
|
||||
m_termination_handler(type::get_shared());
|
||||
} catch (std::exception const & e) {
|
||||
m_elog.write(log::elevel::warn,
|
||||
m_elog->write(log::elevel::warn,
|
||||
std::string("termination_handler call failed. Reason was: ")+e.what());
|
||||
}
|
||||
}
|
||||
@@ -1784,7 +1788,7 @@ void connection<config>::handle_terminate(terminate_status tstat,
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::write_frame() {
|
||||
//m_alog.write(log::alevel::devel,"connection write_frame");
|
||||
//m_alog->write(log::alevel::devel,"connection write_frame");
|
||||
|
||||
{
|
||||
scoped_lock_type lock(m_write_lock);
|
||||
@@ -1830,8 +1834,8 @@ void connection<config>::write_frame() {
|
||||
}
|
||||
|
||||
// Print detailed send stats if those log levels are enabled
|
||||
if (m_alog.static_test(log::alevel::frame_header)) {
|
||||
if (m_alog.dynamic_test(log::alevel::frame_header)) {
|
||||
if (m_alog->static_test(log::alevel::frame_header)) {
|
||||
if (m_alog->dynamic_test(log::alevel::frame_header)) {
|
||||
std::stringstream general,header,payload;
|
||||
|
||||
general << "Dispatching write containing " << m_current_msgs.size()
|
||||
@@ -1851,8 +1855,8 @@ void connection<config>::write_frame() {
|
||||
<< m_current_msgs[i]->get_header().size() << ") "
|
||||
<< utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
|
||||
|
||||
if (m_alog.static_test(log::alevel::frame_payload)) {
|
||||
if (m_alog.dynamic_test(log::alevel::frame_payload)) {
|
||||
if (m_alog->static_test(log::alevel::frame_payload)) {
|
||||
if (m_alog->dynamic_test(log::alevel::frame_payload)) {
|
||||
payload << "[" << i << "] ("
|
||||
<< m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
|
||||
<< (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
|
||||
@@ -1866,9 +1870,9 @@ void connection<config>::write_frame() {
|
||||
|
||||
general << hbytes << " header bytes and " << pbytes << " payload bytes";
|
||||
|
||||
m_alog.write(log::alevel::frame_header,general.str());
|
||||
m_alog.write(log::alevel::frame_header,header.str());
|
||||
m_alog.write(log::alevel::frame_payload,payload.str());
|
||||
m_alog->write(log::alevel::frame_header,general.str());
|
||||
m_alog->write(log::alevel::frame_header,header.str());
|
||||
m_alog->write(log::alevel::frame_payload,payload.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1881,8 +1885,8 @@ void connection<config>::write_frame() {
|
||||
template <typename config>
|
||||
void connection<config>::handle_write_frame(lib::error_code const & ec)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"connection handle_write_frame");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"connection handle_write_frame");
|
||||
}
|
||||
|
||||
bool terminal = m_current_msgs.back()->get_terminal();
|
||||
@@ -1929,21 +1933,21 @@ std::vector<int> const & connection<config>::get_supported_versions() const
|
||||
template <typename config>
|
||||
void connection<config>::process_control_frame(typename config::message_type::ptr msg)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"process_control_frame");
|
||||
m_alog->write(log::alevel::devel,"process_control_frame");
|
||||
|
||||
frame::opcode::value op = msg->get_opcode();
|
||||
lib::error_code ec;
|
||||
|
||||
std::stringstream s;
|
||||
s << "Control frame received with opcode " << op;
|
||||
m_alog.write(log::alevel::control,s.str());
|
||||
m_alog->write(log::alevel::control,s.str());
|
||||
|
||||
if (m_state == session::state::closed) {
|
||||
m_elog.write(log::elevel::warn,"got frame in state closed");
|
||||
m_elog->write(log::elevel::warn,"got frame in state closed");
|
||||
return;
|
||||
}
|
||||
if (op != frame::opcode::CLOSE && m_state != session::state::open) {
|
||||
m_elog.write(log::elevel::warn,"got non-close frame in state closing");
|
||||
m_elog->write(log::elevel::warn,"got non-close frame in state closing");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1968,7 +1972,7 @@ void connection<config>::process_control_frame(typename config::message_type::pt
|
||||
m_ping_timer->cancel();
|
||||
}
|
||||
} else if (op == frame::opcode::CLOSE) {
|
||||
m_alog.write(log::alevel::devel,"got close frame");
|
||||
m_alog->write(log::alevel::devel,"got close frame");
|
||||
// record close code and reason somewhere
|
||||
|
||||
m_remote_close_code = close::extract_code(msg->get_payload(),ec);
|
||||
@@ -1977,12 +1981,12 @@ void connection<config>::process_control_frame(typename config::message_type::pt
|
||||
if (config::drop_on_protocol_error) {
|
||||
s << "Received invalid close code " << m_remote_close_code
|
||||
<< " dropping connection per config.";
|
||||
m_elog.write(log::elevel::devel,s.str());
|
||||
m_elog->write(log::elevel::devel,s.str());
|
||||
this->terminate(ec);
|
||||
} else {
|
||||
s << "Received invalid close code " << m_remote_close_code
|
||||
<< " sending acknowledgement and closing";
|
||||
m_elog.write(log::elevel::devel,s.str());
|
||||
m_elog->write(log::elevel::devel,s.str());
|
||||
ec = send_close_ack(close::status::protocol_error,
|
||||
"Invalid close code");
|
||||
if (ec) {
|
||||
@@ -1995,11 +1999,11 @@ void connection<config>::process_control_frame(typename config::message_type::pt
|
||||
m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
|
||||
if (ec) {
|
||||
if (config::drop_on_protocol_error) {
|
||||
m_elog.write(log::elevel::devel,
|
||||
m_elog->write(log::elevel::devel,
|
||||
"Received invalid close reason. Dropping connection per config");
|
||||
this->terminate(ec);
|
||||
} else {
|
||||
m_elog.write(log::elevel::devel,
|
||||
m_elog->write(log::elevel::devel,
|
||||
"Received invalid close reason. Sending acknowledgement and closing");
|
||||
ec = send_close_ack(close::status::protocol_error,
|
||||
"Invalid close reason");
|
||||
@@ -2014,7 +2018,7 @@ void connection<config>::process_control_frame(typename config::message_type::pt
|
||||
s.str("");
|
||||
s << "Received close frame with code " << m_remote_close_code
|
||||
<< " and reason " << m_remote_close_reason;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
|
||||
ec = send_close_ack();
|
||||
if (ec) {
|
||||
@@ -2022,7 +2026,7 @@ void connection<config>::process_control_frame(typename config::message_type::pt
|
||||
}
|
||||
} else if (m_state == session::state::closing && !m_was_clean) {
|
||||
// ack of our close
|
||||
m_alog.write(log::alevel::devel, "Got acknowledgement of close");
|
||||
m_alog->write(log::alevel::devel, "Got acknowledgement of close");
|
||||
|
||||
m_was_clean = true;
|
||||
|
||||
@@ -2038,11 +2042,11 @@ void connection<config>::process_control_frame(typename config::message_type::pt
|
||||
}
|
||||
} else {
|
||||
// spurious, ignore
|
||||
m_elog.write(log::elevel::devel, "Got close frame in wrong state");
|
||||
m_elog->write(log::elevel::devel, "Got close frame in wrong state");
|
||||
}
|
||||
} else {
|
||||
// got an invalid control opcode
|
||||
m_elog.write(log::elevel::devel, "Got control frame with invalid opcode");
|
||||
m_elog->write(log::elevel::devel, "Got control frame with invalid opcode");
|
||||
// initiate protocol error shutdown
|
||||
}
|
||||
}
|
||||
@@ -2058,7 +2062,7 @@ template <typename config>
|
||||
lib::error_code connection<config>::send_close_frame(close::status::value code,
|
||||
std::string const & reason, bool ack, bool terminal)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"send_close_frame");
|
||||
m_alog->write(log::alevel::devel,"send_close_frame");
|
||||
|
||||
// check for special codes
|
||||
|
||||
@@ -2069,24 +2073,24 @@ lib::error_code connection<config>::send_close_frame(close::status::value code,
|
||||
// send blank info. If it is an ack then echo the close information from
|
||||
// the remote endpoint.
|
||||
if (config::silent_close) {
|
||||
m_alog.write(log::alevel::devel,"closing silently");
|
||||
m_alog->write(log::alevel::devel,"closing silently");
|
||||
m_local_close_code = close::status::no_status;
|
||||
m_local_close_reason = "";
|
||||
m_local_close_reason.clear();
|
||||
} else if (code != close::status::blank) {
|
||||
m_alog.write(log::alevel::devel,"closing with specified codes");
|
||||
m_alog->write(log::alevel::devel,"closing with specified codes");
|
||||
m_local_close_code = code;
|
||||
m_local_close_reason = reason;
|
||||
} else if (!ack) {
|
||||
m_alog.write(log::alevel::devel,"closing with no status code");
|
||||
m_alog->write(log::alevel::devel,"closing with no status code");
|
||||
m_local_close_code = close::status::no_status;
|
||||
m_local_close_reason = "";
|
||||
m_local_close_reason.clear();
|
||||
} else if (m_remote_close_code == close::status::no_status) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"acknowledging a no-status close with normal code");
|
||||
m_local_close_code = close::status::normal;
|
||||
m_local_close_reason = "";
|
||||
m_local_close_reason.clear();
|
||||
} else {
|
||||
m_alog.write(log::alevel::devel,"acknowledging with remote codes");
|
||||
m_alog->write(log::alevel::devel,"acknowledging with remote codes");
|
||||
m_local_close_code = m_remote_close_code;
|
||||
m_local_close_reason = m_remote_close_reason;
|
||||
}
|
||||
@@ -2094,7 +2098,7 @@ lib::error_code connection<config>::send_close_frame(close::status::value code,
|
||||
std::stringstream s;
|
||||
s << "Closing with code: " << m_local_close_code << ", and reason: "
|
||||
<< m_local_close_reason;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
|
||||
message_ptr msg = m_msg_manager->get_message();
|
||||
if (!msg) {
|
||||
@@ -2209,11 +2213,11 @@ void connection<config>::write_push(typename config::message_type::ptr msg)
|
||||
m_send_buffer_size += msg->get_payload().size();
|
||||
m_send_queue.push(msg);
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "write_push: message count: " << m_send_queue.size()
|
||||
<< " buffer size: " << m_send_buffer_size;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2231,11 +2235,11 @@ typename config::message_type::ptr connection<config>::write_pop()
|
||||
m_send_buffer_size -= msg->get_payload().size();
|
||||
m_send_queue.pop();
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "write_pop: message count: " << m_send_queue.size()
|
||||
<< " buffer size: " << m_send_buffer_size;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
@@ -2265,7 +2269,7 @@ void connection<config>::log_open_result()
|
||||
|
||||
// User Agent
|
||||
std::string ua = m_request.get_header("User-Agent");
|
||||
if (ua == "") {
|
||||
if (ua.empty()) {
|
||||
s << "\"\" ";
|
||||
} else {
|
||||
// check if there are any quotes in the user agent
|
||||
@@ -2278,7 +2282,7 @@ void connection<config>::log_open_result()
|
||||
// Status code
|
||||
s << m_response.get_status_code();
|
||||
|
||||
m_alog.write(log::alevel::connect,s.str());
|
||||
m_alog->write(log::alevel::connect,s.str());
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
@@ -2288,11 +2292,11 @@ void connection<config>::log_close_result()
|
||||
|
||||
s << "Disconnect "
|
||||
<< "close local:[" << m_local_close_code
|
||||
<< (m_local_close_reason == "" ? "" : ","+m_local_close_reason)
|
||||
<< (m_local_close_reason.empty() ? "" : ","+m_local_close_reason)
|
||||
<< "] remote:[" << m_remote_close_code
|
||||
<< (m_remote_close_reason == "" ? "" : ","+m_remote_close_reason) << "]";
|
||||
<< (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]";
|
||||
|
||||
m_alog.write(log::alevel::disconnect,s.str());
|
||||
m_alog->write(log::alevel::disconnect,s.str());
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
@@ -2315,7 +2319,7 @@ void connection<config>::log_fail_result()
|
||||
|
||||
// User Agent
|
||||
std::string ua = m_request.get_header("User-Agent");
|
||||
if (ua == "") {
|
||||
if (ua.empty()) {
|
||||
s << " \"\" ";
|
||||
} else {
|
||||
// check if there are any quotes in the user agent
|
||||
@@ -2331,7 +2335,7 @@ void connection<config>::log_fail_result()
|
||||
// WebSocket++ error code & reason
|
||||
s << " " << m_ec << " " << m_ec.message();
|
||||
|
||||
m_alog.write(log::alevel::fail,s.str());
|
||||
m_alog->write(log::alevel::fail,s.str());
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
@@ -2339,12 +2343,12 @@ void connection<config>::log_http_result() {
|
||||
std::stringstream s;
|
||||
|
||||
if (processor::is_websocket_handshake(m_request)) {
|
||||
m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket");
|
||||
m_alog->write(log::alevel::devel,"Call to log_http_result for WebSocket");
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection Type
|
||||
s << (m_request.get_header("host") == "" ? "-" : m_request.get_header("host"))
|
||||
s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host"))
|
||||
<< " " << transport_con_type::get_remote_endpoint()
|
||||
<< " \"" << m_request.get_method()
|
||||
<< " " << (m_uri ? m_uri->get_resource() : "-")
|
||||
@@ -2353,14 +2357,14 @@ void connection<config>::log_http_result() {
|
||||
|
||||
// User Agent
|
||||
std::string ua = m_request.get_header("User-Agent");
|
||||
if (ua == "") {
|
||||
if (ua.empty()) {
|
||||
s << " \"\" ";
|
||||
} else {
|
||||
// check if there are any quotes in the user agent
|
||||
s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
|
||||
}
|
||||
|
||||
m_alog.write(log::alevel::http,s.str());
|
||||
m_alog->write(log::alevel::http,s.str());
|
||||
}
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace websocketpp {
|
||||
template <typename connection, typename config>
|
||||
typename endpoint<connection,config>::connection_ptr
|
||||
endpoint<connection,config>::create_connection() {
|
||||
m_alog.write(log::alevel::devel,"create_connection");
|
||||
m_alog->write(log::alevel::devel,"create_connection");
|
||||
//scoped_lock_type lock(m_state_lock);
|
||||
|
||||
/*if (m_state == STOPPING || m_state == STOPPED) {
|
||||
@@ -45,7 +45,7 @@ endpoint<connection,config>::create_connection() {
|
||||
//scoped_lock_type guard(m_mutex);
|
||||
// Create a connection on the heap and manage it using a shared pointer
|
||||
connection_ptr con = lib::make_shared<connection_type>(m_is_server,
|
||||
m_user_agent, lib::ref(m_alog), lib::ref(m_elog), lib::ref(m_rng));
|
||||
m_user_agent, m_alog, m_elog, lib::ref(m_rng));
|
||||
|
||||
connection_weak_ptr w(con);
|
||||
|
||||
@@ -85,7 +85,7 @@ endpoint<connection,config>::create_connection() {
|
||||
|
||||
ec = transport_type::init(con);
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::fatal,ec.message());
|
||||
m_elog->write(log::elevel::fatal,ec.message());
|
||||
return connection_ptr();
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ void endpoint<connection,config>::interrupt(connection_hdl hdl, lib::error_code
|
||||
connection_ptr con = get_con_from_hdl(hdl,ec);
|
||||
if (ec) {return;}
|
||||
|
||||
m_alog.write(log::alevel::devel,"Interrupting connection");
|
||||
m_alog->write(log::alevel::devel,"Interrupting connection");
|
||||
|
||||
ec = con->interrupt();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP
|
||||
|
||||
#include <websocketpp/frame.hpp>
|
||||
#include <websocketpp/http/constants.hpp>
|
||||
|
||||
#include <websocketpp/utf8_validator.hpp>
|
||||
#include <websocketpp/common/network.hpp>
|
||||
#include <websocketpp/common/md5.hpp>
|
||||
@@ -85,9 +87,9 @@ public:
|
||||
// Host is required by HTTP/1.1
|
||||
// Connection is required by is_websocket_handshake
|
||||
// Upgrade is required by is_websocket_handshake
|
||||
if (r.get_header("Sec-WebSocket-Key1") == "" ||
|
||||
r.get_header("Sec-WebSocket-Key2") == "" ||
|
||||
r.get_header("Sec-WebSocket-Key3") == "")
|
||||
if (r.get_header("Sec-WebSocket-Key1").empty() ||
|
||||
r.get_header("Sec-WebSocket-Key2").empty() ||
|
||||
r.get_header("Sec-WebSocket-Key3").empty())
|
||||
{
|
||||
return make_error_code(error::missing_required_header);
|
||||
}
|
||||
@@ -126,18 +128,18 @@ public:
|
||||
|
||||
// Echo back client's origin unless our local application set a
|
||||
// more restrictive one.
|
||||
if (res.get_header("Sec-WebSocket-Origin") == "") {
|
||||
if (res.get_header("Sec-WebSocket-Origin").empty()) {
|
||||
res.append_header("Sec-WebSocket-Origin",req.get_header("Origin"));
|
||||
}
|
||||
|
||||
// Echo back the client's request host unless our local application
|
||||
// set a different one.
|
||||
if (res.get_header("Sec-WebSocket-Location") == "") {
|
||||
if (res.get_header("Sec-WebSocket-Location").empty()) {
|
||||
uri_ptr uri = get_uri(req);
|
||||
res.append_header("Sec-WebSocket-Location",uri->str());
|
||||
}
|
||||
|
||||
if (subprotocol != "") {
|
||||
if (!subprotocol.empty()) {
|
||||
res.replace_header("Sec-WebSocket-Protocol",subprotocol);
|
||||
}
|
||||
|
||||
@@ -186,15 +188,29 @@ public:
|
||||
|
||||
/// Extracts requested subprotocols from a handshake request
|
||||
/**
|
||||
* hybi00 doesn't support subprotocols so there never will be any requested
|
||||
* hybi00 does support subprotocols
|
||||
* https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-1.9
|
||||
*
|
||||
* @param [in] req The request to extract from
|
||||
* @param [out] subprotocol_list A reference to a vector of strings to store
|
||||
* the results in.
|
||||
*/
|
||||
lib::error_code extract_subprotocols(request_type const &,
|
||||
std::vector<std::string> &)
|
||||
lib::error_code extract_subprotocols(request_type const & req,
|
||||
std::vector<std::string> & subprotocol_list)
|
||||
{
|
||||
if (!req.get_header("Sec-WebSocket-Protocol").empty()) {
|
||||
http::parameter_list p;
|
||||
|
||||
if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) {
|
||||
http::parameter_list::const_iterator it;
|
||||
|
||||
for (it = p.begin(); it != p.end(); ++it) {
|
||||
subprotocol_list.push_back(it->first);
|
||||
}
|
||||
} else {
|
||||
return error::make_error_code(error::subprotocol_parse_error);
|
||||
}
|
||||
}
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
@@ -400,7 +416,7 @@ public:
|
||||
private:
|
||||
void decode_client_key(std::string const & key, char * result) const {
|
||||
unsigned int spaces = 0;
|
||||
std::string digits = "";
|
||||
std::string digits;
|
||||
uint32_t num;
|
||||
|
||||
// key2
|
||||
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
// Host is required by HTTP/1.1
|
||||
// Connection is required by is_websocket_handshake
|
||||
// Upgrade is required by is_websocket_handshake
|
||||
if (r.get_header("Sec-WebSocket-Key") == "") {
|
||||
if (r.get_header("Sec-WebSocket-Key").empty()) {
|
||||
return make_error_code(error::missing_required_header);
|
||||
}
|
||||
|
||||
@@ -574,19 +574,6 @@ public:
|
||||
* Performs validation, masking, compression, etc. will return an error if
|
||||
* there was an error, otherwise msg will be ready to be written
|
||||
*
|
||||
* By default WebSocket++ performs block masking/unmasking in a manner that
|
||||
* makes assumptions about the nature of the machine and STL library used.
|
||||
* In particular the assumption is either a 32 or 64 bit word size and an
|
||||
* STL with std::string::data returning a contiguous char array.
|
||||
*
|
||||
* This method improves masking performance by 3-8x depending on the ratio
|
||||
* of small to large messages and the availability of a 64 bit processor.
|
||||
*
|
||||
* To disable this optimization (for use with alternative STL
|
||||
* implementations or processors) define WEBSOCKETPP_STRICT_MASKING when
|
||||
* compiling the library. This will force the library to perform masking in
|
||||
* single byte chunks.
|
||||
*
|
||||
* TODO: tests
|
||||
*
|
||||
* @param in An unprepared message to prepare
|
||||
@@ -791,19 +778,9 @@ protected:
|
||||
{
|
||||
// unmask if masked
|
||||
if (frame::get_masked(m_basic_header)) {
|
||||
#ifdef WEBSOCKETPP_STRICT_MASKING
|
||||
m_current_msg->prepared_key = frame::byte_mask_circ(
|
||||
buf,
|
||||
len,
|
||||
m_current_msg->prepared_key
|
||||
);
|
||||
#else
|
||||
m_current_msg->prepared_key = frame::word_mask_circ(
|
||||
buf,
|
||||
len,
|
||||
m_current_msg->prepared_key
|
||||
);
|
||||
#endif
|
||||
m_current_msg->prepared_key = frame::byte_mask_circ(
|
||||
buf, len, m_current_msg->prepared_key);
|
||||
// TODO: SIMD masking
|
||||
}
|
||||
|
||||
std::string & out = m_current_msg->msg_ptr->get_raw_payload();
|
||||
@@ -962,16 +939,8 @@ protected:
|
||||
void masked_copy (std::string const & i, std::string & o,
|
||||
frame::masking_key_type key) const
|
||||
{
|
||||
#ifdef WEBSOCKETPP_STRICT_MASKING
|
||||
frame::byte_mask(i.begin(),i.end(),o.begin(),key);
|
||||
#else
|
||||
websocketpp::frame::word_mask_exact(
|
||||
reinterpret_cast<uint8_t *>(const_cast<char *>(i.data())),
|
||||
reinterpret_cast<uint8_t *>(const_cast<char *>(o.data())),
|
||||
i.size(),
|
||||
key
|
||||
);
|
||||
#endif
|
||||
frame::byte_mask(i.begin(),i.end(),o.begin(),key);
|
||||
// TODO: SIMD masking
|
||||
}
|
||||
|
||||
/// Generic prepare control frame with opcode and payload.
|
||||
|
||||
@@ -109,7 +109,7 @@ int get_websocket_version(request_type& r) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (r.get_header("Sec-WebSocket-Version") == "") {
|
||||
if (r.get_header("Sec-WebSocket-Version").empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,9 +67,11 @@ public:
|
||||
/// Type of the endpoint component of this server
|
||||
typedef endpoint<connection_type,config> endpoint_type;
|
||||
|
||||
friend class connection<config>;
|
||||
|
||||
explicit client() : endpoint_type(false)
|
||||
{
|
||||
endpoint_type::m_alog.write(log::alevel::devel, "client constructor");
|
||||
endpoint_type::m_alog->write(log::alevel::devel, "client constructor");
|
||||
}
|
||||
|
||||
/// Get a new connection
|
||||
@@ -155,10 +157,10 @@ private:
|
||||
if (ec) {
|
||||
con->terminate(ec);
|
||||
|
||||
endpoint_type::m_elog.write(log::elevel::rerror,
|
||||
endpoint_type::m_elog->write(log::elevel::rerror,
|
||||
"handle_connect error: "+ec.message());
|
||||
} else {
|
||||
endpoint_type::m_alog.write(log::alevel::connect,
|
||||
endpoint_type::m_alog->write(log::alevel::connect,
|
||||
"Successful connection");
|
||||
|
||||
con->start();
|
||||
|
||||
@@ -64,9 +64,11 @@ public:
|
||||
/// Type of the endpoint component of this server
|
||||
typedef endpoint<connection_type,config> endpoint_type;
|
||||
|
||||
friend class connection<config>;
|
||||
|
||||
explicit server() : endpoint_type(true)
|
||||
{
|
||||
endpoint_type::m_alog.write(log::alevel::devel, "server constructor");
|
||||
endpoint_type::m_alog->write(log::alevel::devel, "server constructor");
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
@@ -75,7 +77,7 @@ public:
|
||||
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
|
||||
// no copy constructor because endpoints are not copyable
|
||||
server<config>(server<config> &) = delete;
|
||||
|
||||
|
||||
// no copy assignment operator because endpoints are not copyable
|
||||
server<config> & operator=(server<config> const &) = delete;
|
||||
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
|
||||
@@ -113,7 +115,7 @@ public:
|
||||
*
|
||||
* Refer to documentation for the transport policy you are using for
|
||||
* instructions on how to stop this acceptance loop.
|
||||
*
|
||||
*
|
||||
* @param [out] ec A status code indicating an error, if any.
|
||||
*/
|
||||
void start_accept(lib::error_code & ec) {
|
||||
@@ -124,13 +126,18 @@ public:
|
||||
|
||||
ec = lib::error_code();
|
||||
connection_ptr con = get_connection();
|
||||
|
||||
|
||||
if (!con) {
|
||||
ec = error::make_error_code(error::con_creation_failed);
|
||||
return;
|
||||
}
|
||||
|
||||
transport_type::async_accept(
|
||||
lib::static_pointer_cast<transport_con_type>(con),
|
||||
lib::bind(&type::handle_accept,this,con,lib::placeholders::_1),
|
||||
ec
|
||||
);
|
||||
|
||||
|
||||
if (ec && con) {
|
||||
// If the connection was constructed but the accept failed,
|
||||
// terminate the connection to prevent memory leaks
|
||||
@@ -161,10 +168,10 @@ public:
|
||||
con->terminate(ec);
|
||||
|
||||
if (ec == error::operation_canceled) {
|
||||
endpoint_type::m_elog.write(log::elevel::info,
|
||||
endpoint_type::m_elog->write(log::elevel::info,
|
||||
"handle_accept error: "+ec.message());
|
||||
} else {
|
||||
endpoint_type::m_elog.write(log::elevel::rerror,
|
||||
endpoint_type::m_elog->write(log::elevel::rerror,
|
||||
"handle_accept error: "+ec.message());
|
||||
}
|
||||
} else {
|
||||
@@ -174,10 +181,10 @@ public:
|
||||
lib::error_code start_ec;
|
||||
start_accept(start_ec);
|
||||
if (start_ec == error::async_accept_not_listening) {
|
||||
endpoint_type::m_elog.write(log::elevel::info,
|
||||
endpoint_type::m_elog->write(log::elevel::info,
|
||||
"Stopping acceptance of new connections because the underlying transport is no longer listening.");
|
||||
} else if (start_ec) {
|
||||
endpoint_type::m_elog.write(log::elevel::rerror,
|
||||
endpoint_type::m_elog->write(log::elevel::rerror,
|
||||
"Restarting async_accept loop failed: "+ec.message());
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
@@ -142,9 +142,6 @@ inline custom_alloc_handler<Handler> make_custom_alloc_handler(
|
||||
template <typename config>
|
||||
class endpoint;
|
||||
|
||||
typedef lib::function<void(lib::asio::error_code const &)>
|
||||
socket_shutdown_handler;
|
||||
|
||||
typedef lib::function<void (lib::asio::error_code const & ec,
|
||||
size_t bytes_transferred)> async_read_handler;
|
||||
|
||||
|
||||
@@ -98,12 +98,12 @@ public:
|
||||
friend class endpoint<config>;
|
||||
|
||||
// generate and manage our own io_service
|
||||
explicit connection(bool is_server, alog_type & alog, elog_type & elog)
|
||||
explicit connection(bool is_server, const lib::shared_ptr<alog_type> & alog, const lib::shared_ptr<elog_type> & elog)
|
||||
: m_is_server(is_server)
|
||||
, m_alog(alog)
|
||||
, m_elog(elog)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"asio con transport constructor");
|
||||
m_alog->write(log::alevel::devel,"asio con transport constructor");
|
||||
}
|
||||
|
||||
/// Get a shared pointer to this component
|
||||
@@ -284,7 +284,7 @@ public:
|
||||
std::string ret = socket_con_type::get_remote_endpoint(ec);
|
||||
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::info,ret);
|
||||
m_elog->write(log::elevel::info,ret);
|
||||
return "Unknown";
|
||||
} else {
|
||||
return ret;
|
||||
@@ -366,6 +366,29 @@ public:
|
||||
return m_strand;
|
||||
}
|
||||
|
||||
/// Get the internal transport error code for a closed/failed connection
|
||||
/**
|
||||
* Retrieves a machine readable detailed error code indicating the reason
|
||||
* that the connection was closed or failed. Valid only after the close or
|
||||
* fail handler is called.
|
||||
*
|
||||
* Primarily used if you are using mismatched asio / system_error
|
||||
* implementations such as `boost::asio` with `std::system_error`. In these
|
||||
* cases the transport error type is different than the library error type
|
||||
* and some WebSocket++ functions that return transport errors via the
|
||||
* library error code type will be coerced into a catch all `pass_through`
|
||||
* or `tls_error` error. This method will return the original machine
|
||||
* readable transport error in the native type.
|
||||
*
|
||||
* @since 0.7.0
|
||||
*
|
||||
* @return Error code indicating the reason the connection was closed or
|
||||
* failed
|
||||
*/
|
||||
lib::asio::error_code get_transport_ec() const {
|
||||
return m_tec;
|
||||
}
|
||||
|
||||
/// Initialize transport for reading
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
@@ -385,19 +408,18 @@ public:
|
||||
*/
|
||||
protected:
|
||||
void init(init_handler callback) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"asio connection init");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"asio connection init");
|
||||
}
|
||||
|
||||
// TODO: pre-init timeout. Right now no implemented socket policies
|
||||
// actually have an asyncronous pre-init
|
||||
|
||||
m_init_handler = callback;
|
||||
|
||||
socket_con_type::pre_init(
|
||||
lib::bind(
|
||||
&type::handle_pre_init,
|
||||
get_shared(),
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
@@ -439,40 +461,19 @@ protected:
|
||||
m_io_service = io_service;
|
||||
|
||||
if (config::enable_multithreading) {
|
||||
m_strand = lib::make_shared<lib::asio::strand>(
|
||||
m_strand = lib::make_shared<lib::asio::io_service::strand>(
|
||||
lib::ref(*io_service));
|
||||
|
||||
m_async_read_handler = m_strand->wrap(lib::bind(
|
||||
&type::handle_async_read, get_shared(),lib::placeholders::_1,
|
||||
lib::placeholders::_2));
|
||||
|
||||
m_async_write_handler = m_strand->wrap(lib::bind(
|
||||
&type::handle_async_write, get_shared(),lib::placeholders::_1,
|
||||
lib::placeholders::_2));
|
||||
} else {
|
||||
m_async_read_handler = lib::bind(&type::handle_async_read,
|
||||
get_shared(), lib::placeholders::_1, lib::placeholders::_2);
|
||||
|
||||
m_async_write_handler = lib::bind(&type::handle_async_write,
|
||||
get_shared(), lib::placeholders::_1, lib::placeholders::_2);
|
||||
}
|
||||
|
||||
lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
|
||||
m_is_server);
|
||||
|
||||
if (ec) {
|
||||
// reset the handlers to break the circular reference:
|
||||
// this->handler->this
|
||||
lib::clear_function(m_async_read_handler);
|
||||
lib::clear_function(m_async_write_handler);
|
||||
}
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
void handle_pre_init(lib::error_code const & ec) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"asio connection handle pre_init");
|
||||
void handle_pre_init(init_handler callback, lib::error_code const & ec) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"asio connection handle pre_init");
|
||||
}
|
||||
|
||||
if (m_tcp_pre_init_handler) {
|
||||
@@ -480,21 +481,21 @@ protected:
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
m_init_handler(ec);
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
// If we have a proxy set issue a proxy connect, otherwise skip to
|
||||
// post_init
|
||||
if (!m_proxy.empty()) {
|
||||
proxy_write();
|
||||
proxy_write(callback);
|
||||
} else {
|
||||
post_init();
|
||||
post_init(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void post_init() {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"asio connection post_init");
|
||||
void post_init(init_handler callback) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"asio connection post_init");
|
||||
}
|
||||
|
||||
timer_ptr post_timer;
|
||||
@@ -506,7 +507,7 @@ protected:
|
||||
&type::handle_post_init_timeout,
|
||||
get_shared(),
|
||||
post_timer,
|
||||
m_init_handler,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
@@ -517,7 +518,7 @@ protected:
|
||||
&type::handle_post_init,
|
||||
get_shared(),
|
||||
post_timer,
|
||||
m_init_handler,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
@@ -539,7 +540,7 @@ protected:
|
||||
|
||||
if (ec) {
|
||||
if (ec == transport::error::operation_aborted) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio post init timer cancelled");
|
||||
return;
|
||||
}
|
||||
@@ -554,8 +555,8 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
m_alog.write(log::alevel::devel,"Asio transport post-init timed out");
|
||||
socket_con_type::cancel_socket();
|
||||
m_alog->write(log::alevel::devel, "Asio transport post-init timed out");
|
||||
cancel_socket_checked();
|
||||
callback(ret_ec);
|
||||
}
|
||||
|
||||
@@ -574,7 +575,7 @@ protected:
|
||||
if (ec == transport::error::operation_aborted ||
|
||||
(post_timer && lib::asio::is_neg(post_timer->expires_from_now())))
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"post_init cancelled");
|
||||
m_alog->write(log::alevel::devel,"post_init cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -582,8 +583,8 @@ protected:
|
||||
post_timer->cancel();
|
||||
}
|
||||
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"asio connection handle_post_init");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"asio connection handle_post_init");
|
||||
}
|
||||
|
||||
if (m_tcp_post_init_handler) {
|
||||
@@ -593,15 +594,15 @@ protected:
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
void proxy_write() {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"asio connection proxy_write");
|
||||
void proxy_write(init_handler callback) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"asio connection proxy_write");
|
||||
}
|
||||
|
||||
if (!m_proxy_data) {
|
||||
m_elog.write(log::elevel::library,
|
||||
m_elog->write(log::elevel::library,
|
||||
"assertion failed: !m_proxy_data in asio::connection::proxy_write");
|
||||
m_init_handler(make_error_code(error::general));
|
||||
callback(make_error_code(error::general));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -610,7 +611,7 @@ protected:
|
||||
m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(),
|
||||
m_proxy_data->write_buf.size()));
|
||||
|
||||
m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
|
||||
m_alog->write(log::alevel::devel,m_proxy_data->write_buf);
|
||||
|
||||
// Set a timer so we don't wait forever for the proxy to respond
|
||||
m_proxy_data->timer = this->set_timer(
|
||||
@@ -618,7 +619,7 @@ protected:
|
||||
lib::bind(
|
||||
&type::handle_proxy_timeout,
|
||||
get_shared(),
|
||||
m_init_handler,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
@@ -630,7 +631,7 @@ protected:
|
||||
m_bufs,
|
||||
m_strand->wrap(lib::bind(
|
||||
&type::handle_proxy_write, get_shared(),
|
||||
m_init_handler,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
))
|
||||
);
|
||||
@@ -640,7 +641,7 @@ protected:
|
||||
m_bufs,
|
||||
lib::bind(
|
||||
&type::handle_proxy_write, get_shared(),
|
||||
m_init_handler,
|
||||
callback,
|
||||
lib::placeholders::_1
|
||||
)
|
||||
);
|
||||
@@ -650,16 +651,16 @@ protected:
|
||||
void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
|
||||
{
|
||||
if (ec == transport::error::operation_aborted) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio handle_proxy_write timer cancelled");
|
||||
return;
|
||||
} else if (ec) {
|
||||
log_err(log::elevel::devel,"asio handle_proxy_write",ec);
|
||||
callback(ec);
|
||||
} else {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio handle_proxy_write timer expired");
|
||||
socket_con_type::cancel_socket();
|
||||
cancel_socket_checked();
|
||||
callback(make_error_code(transport::error::timeout));
|
||||
}
|
||||
}
|
||||
@@ -667,8 +668,8 @@ protected:
|
||||
void handle_proxy_write(init_handler callback,
|
||||
lib::asio::error_code const & ec)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio connection handle_proxy_write");
|
||||
}
|
||||
|
||||
@@ -680,7 +681,7 @@ protected:
|
||||
if (ec == lib::asio::error::operation_aborted ||
|
||||
lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
|
||||
{
|
||||
m_elog.write(log::elevel::devel,"write operation aborted");
|
||||
m_elog->write(log::elevel::devel,"write operation aborted");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -695,12 +696,12 @@ protected:
|
||||
}
|
||||
|
||||
void proxy_read(init_handler callback) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"asio connection proxy_read");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"asio connection proxy_read");
|
||||
}
|
||||
|
||||
if (!m_proxy_data) {
|
||||
m_elog.write(log::elevel::library,
|
||||
m_elog->write(log::elevel::library,
|
||||
"assertion failed: !m_proxy_data in asio::connection::proxy_read");
|
||||
m_proxy_data->timer->cancel();
|
||||
callback(make_error_code(error::general));
|
||||
@@ -741,8 +742,8 @@ protected:
|
||||
void handle_proxy_read(init_handler callback,
|
||||
lib::asio::error_code const & ec, size_t)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio connection handle_proxy_read");
|
||||
}
|
||||
|
||||
@@ -752,7 +753,7 @@ protected:
|
||||
if (ec == lib::asio::error::operation_aborted ||
|
||||
lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
|
||||
{
|
||||
m_elog.write(log::elevel::devel,"read operation aborted");
|
||||
m_elog->write(log::elevel::devel,"read operation aborted");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -760,12 +761,12 @@ protected:
|
||||
m_proxy_data->timer->cancel();
|
||||
|
||||
if (ec) {
|
||||
m_elog.write(log::elevel::info,
|
||||
m_elog->write(log::elevel::info,
|
||||
"asio handle_proxy_read error: "+ec.message());
|
||||
callback(make_error_code(error::pass_through));
|
||||
} else {
|
||||
if (!m_proxy_data) {
|
||||
m_elog.write(log::elevel::library,
|
||||
m_elog->write(log::elevel::library,
|
||||
"assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
|
||||
callback(make_error_code(error::general));
|
||||
return;
|
||||
@@ -782,7 +783,7 @@ protected:
|
||||
return;
|
||||
}
|
||||
|
||||
m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
|
||||
m_alog->write(log::alevel::devel,m_proxy_data->res.raw());
|
||||
|
||||
if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
|
||||
// got an error response back
|
||||
@@ -794,7 +795,7 @@ protected:
|
||||
<< " ("
|
||||
<< m_proxy_data->res.get_status_msg()
|
||||
<< ")";
|
||||
m_elog.write(log::elevel::info,s.str());
|
||||
m_elog->write(log::elevel::info,s.str());
|
||||
callback(make_error_code(error::proxy_failed));
|
||||
return;
|
||||
}
|
||||
@@ -811,63 +812,66 @@ protected:
|
||||
m_proxy_data.reset();
|
||||
|
||||
// Continue with post proxy initialization
|
||||
post_init();
|
||||
post_init(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// read at least num_bytes bytes into buf and then call handler.
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
void async_read_at_least(size_t num_bytes, char *buf, size_t len,
|
||||
read_handler handler)
|
||||
{
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
std::stringstream s;
|
||||
s << "asio async_read_at_least: " << num_bytes;
|
||||
m_alog.write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
if (!m_async_read_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"async_read_at_least called after async_shutdown");
|
||||
handler(make_error_code(transport::error::action_after_shutdown),0);
|
||||
return;
|
||||
m_alog->write(log::alevel::devel,s.str());
|
||||
}
|
||||
|
||||
// TODO: safety vs speed ?
|
||||
// maybe move into an if devel block
|
||||
/*if (num_bytes > len) {
|
||||
m_elog.write(log::elevel::devel,
|
||||
m_elog->write(log::elevel::devel,
|
||||
"asio async_read_at_least error::invalid_num_bytes");
|
||||
handler(make_error_code(transport::error::invalid_num_bytes),
|
||||
size_t(0));
|
||||
return;
|
||||
}*/
|
||||
|
||||
m_read_handler = handler;
|
||||
|
||||
if (!m_read_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"asio con async_read_at_least called with bad handler");
|
||||
if (config::enable_multithreading) {
|
||||
lib::asio::async_read(
|
||||
socket_con_type::get_socket(),
|
||||
lib::asio::buffer(buf,len),
|
||||
lib::asio::transfer_at_least(num_bytes),
|
||||
m_strand->wrap(make_custom_alloc_handler(
|
||||
m_read_handler_allocator,
|
||||
lib::bind(
|
||||
&type::handle_async_read, get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
)
|
||||
))
|
||||
);
|
||||
} else {
|
||||
lib::asio::async_read(
|
||||
socket_con_type::get_socket(),
|
||||
lib::asio::buffer(buf,len),
|
||||
lib::asio::transfer_at_least(num_bytes),
|
||||
make_custom_alloc_handler(
|
||||
m_read_handler_allocator,
|
||||
lib::bind(
|
||||
&type::handle_async_read, get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
lib::asio::async_read(
|
||||
socket_con_type::get_socket(),
|
||||
lib::asio::buffer(buf,len),
|
||||
lib::asio::transfer_at_least(num_bytes),
|
||||
make_custom_alloc_handler(
|
||||
m_read_handler_allocator,
|
||||
m_async_read_handler
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void handle_async_read(lib::asio::error_code const & ec,
|
||||
void handle_async_read(read_handler handler, lib::asio::error_code const & ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
m_alog.write(log::alevel::devel, "asio con handle_async_read");
|
||||
m_alog->write(log::alevel::devel, "asio con handle_async_read");
|
||||
|
||||
// translate asio error codes into more lib::error_codes
|
||||
lib::error_code tec;
|
||||
@@ -877,6 +881,7 @@ protected:
|
||||
// We don't know much more about the error at this point. As our
|
||||
// socket/security policy if it knows more:
|
||||
tec = socket_con_type::translate_ec(ec);
|
||||
m_tec = ec;
|
||||
|
||||
if (tec == transport::error::tls_error ||
|
||||
tec == transport::error::pass_through)
|
||||
@@ -887,63 +892,84 @@ protected:
|
||||
log_err(log::elevel::info,"asio async_read_at_least",ec);
|
||||
}
|
||||
}
|
||||
if (m_read_handler) {
|
||||
m_read_handler(tec,bytes_transferred);
|
||||
// TODO: why does this line break things?
|
||||
//m_read_handler = _WEBSOCKETPP_NULL_FUNCTION_;
|
||||
if (handler) {
|
||||
handler(tec,bytes_transferred);
|
||||
} else {
|
||||
// This can happen in cases where the connection is terminated while
|
||||
// the transport is waiting on a read.
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_async_read called with null read handler");
|
||||
}
|
||||
}
|
||||
|
||||
/// Initiate a potentially asyncronous write of the given buffer
|
||||
void async_write(const char* buf, size_t len, write_handler handler) {
|
||||
if (!m_async_write_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"async_write (single) called after async_shutdown");
|
||||
handler(make_error_code(transport::error::action_after_shutdown));
|
||||
return;
|
||||
}
|
||||
|
||||
m_bufs.push_back(lib::asio::buffer(buf,len));
|
||||
|
||||
m_write_handler = handler;
|
||||
|
||||
lib::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
m_async_write_handler
|
||||
)
|
||||
);
|
||||
if (config::enable_multithreading) {
|
||||
lib::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
m_strand->wrap(make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
lib::bind(
|
||||
&type::handle_async_write, get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
)
|
||||
))
|
||||
);
|
||||
} else {
|
||||
lib::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
lib::bind(
|
||||
&type::handle_async_write, get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initiate a potentially asyncronous write of the given buffers
|
||||
void async_write(std::vector<buffer> const & bufs, write_handler handler) {
|
||||
if (!m_async_write_handler) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"async_write (vector) called after async_shutdown");
|
||||
handler(make_error_code(transport::error::action_after_shutdown));
|
||||
return;
|
||||
}
|
||||
std::vector<buffer>::const_iterator it;
|
||||
|
||||
for (it = bufs.begin(); it != bufs.end(); ++it) {
|
||||
m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len));
|
||||
}
|
||||
|
||||
m_write_handler = handler;
|
||||
|
||||
lib::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
m_async_write_handler
|
||||
)
|
||||
);
|
||||
if (config::enable_multithreading) {
|
||||
lib::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
m_strand->wrap(make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
lib::bind(
|
||||
&type::handle_async_write, get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
)
|
||||
))
|
||||
);
|
||||
} else {
|
||||
lib::asio::async_write(
|
||||
socket_con_type::get_socket(),
|
||||
m_bufs,
|
||||
make_custom_alloc_handler(
|
||||
m_write_handler_allocator,
|
||||
lib::bind(
|
||||
&type::handle_async_write, get_shared(),
|
||||
handler,
|
||||
lib::placeholders::_1, lib::placeholders::_2
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Async write callback
|
||||
@@ -951,21 +977,19 @@ protected:
|
||||
* @param ec The status code
|
||||
* @param bytes_transferred The number of bytes read
|
||||
*/
|
||||
void handle_async_write(lib::asio::error_code const & ec, size_t) {
|
||||
void handle_async_write(write_handler handler, lib::asio::error_code const & ec, size_t) {
|
||||
m_bufs.clear();
|
||||
lib::error_code tec;
|
||||
if (ec) {
|
||||
log_err(log::elevel::info,"asio async_write",ec);
|
||||
tec = make_error_code(transport::error::pass_through);
|
||||
}
|
||||
if (m_write_handler) {
|
||||
m_write_handler(tec);
|
||||
// TODO: why does this line break things?
|
||||
//m_write_handler = _WEBSOCKETPP_NULL_FUNCTION_;
|
||||
if (handler) {
|
||||
handler(tec);
|
||||
} else {
|
||||
// This can happen in cases where the connection is terminated while
|
||||
// the transport is waiting on a read.
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"handle_async_write called with null write handler");
|
||||
}
|
||||
}
|
||||
@@ -1010,20 +1034,10 @@ protected:
|
||||
|
||||
/// close and clean up the underlying socket
|
||||
void async_shutdown(shutdown_handler callback) {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,"asio connection async_shutdown");
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,"asio connection async_shutdown");
|
||||
}
|
||||
|
||||
// Reset cached handlers now that we won't be reading or writing anymore
|
||||
// These cached handlers store shared pointers to this connection and
|
||||
// will leak the connection if not destroyed.
|
||||
lib::clear_function(m_async_read_handler);
|
||||
lib::clear_function(m_async_write_handler);
|
||||
lib::clear_function(m_init_handler);
|
||||
|
||||
lib::clear_function(m_read_handler);
|
||||
lib::clear_function(m_write_handler);
|
||||
|
||||
timer_ptr shutdown_timer;
|
||||
shutdown_timer = set_timer(
|
||||
config::timeout_socket_shutdown,
|
||||
@@ -1060,7 +1074,7 @@ protected:
|
||||
|
||||
if (ec) {
|
||||
if (ec == transport::error::operation_aborted) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio socket shutdown timer cancelled");
|
||||
return;
|
||||
}
|
||||
@@ -1071,9 +1085,9 @@ protected:
|
||||
ret_ec = make_error_code(transport::error::timeout);
|
||||
}
|
||||
|
||||
m_alog.write(log::alevel::devel,
|
||||
m_alog->write(log::alevel::devel,
|
||||
"Asio transport socket shutdown timed out");
|
||||
socket_con_type::cancel_socket();
|
||||
cancel_socket_checked();
|
||||
callback(ret_ec);
|
||||
}
|
||||
|
||||
@@ -1083,7 +1097,7 @@ protected:
|
||||
if (ec == lib::asio::error::operation_aborted ||
|
||||
lib::asio::is_neg(shutdown_timer->expires_from_now()))
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"async_shutdown cancelled");
|
||||
m_alog->write(log::alevel::devel,"async_shutdown cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1100,6 +1114,7 @@ protected:
|
||||
// We don't know anything more about this error, give our
|
||||
// socket/security policy a crack at it.
|
||||
tec = socket_con_type::translate_ec(ec);
|
||||
m_tec = ec;
|
||||
|
||||
if (tec == transport::error::tls_short_read) {
|
||||
// TLS short read at this point is somewhat expected if both
|
||||
@@ -1114,26 +1129,40 @@ protected:
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_alog.static_test(log::alevel::devel)) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
if (m_alog->static_test(log::alevel::devel)) {
|
||||
m_alog->write(log::alevel::devel,
|
||||
"asio con handle_async_shutdown");
|
||||
}
|
||||
}
|
||||
callback(tec);
|
||||
}
|
||||
|
||||
/// Cancel the underlying socket and log any errors
|
||||
void cancel_socket_checked() {
|
||||
lib::asio::error_code cec = socket_con_type::cancel_socket();
|
||||
if (cec) {
|
||||
if (cec == lib::asio::error::operation_not_supported) {
|
||||
// cancel not supported on this OS, ignore and log at dev level
|
||||
m_alog->write(log::alevel::devel, "socket cancel not supported");
|
||||
} else {
|
||||
log_err(log::elevel::warn, "socket cancel failed", cec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Convenience method for logging the code and message for an error_code
|
||||
template <typename error_type>
|
||||
void log_err(log::level l, const char * msg, const error_type & ec) {
|
||||
std::stringstream s;
|
||||
s << msg << " error: " << ec << " (" << ec.message() << ")";
|
||||
m_elog.write(l,s.str());
|
||||
m_elog->write(l,s.str());
|
||||
}
|
||||
|
||||
// static settings
|
||||
const bool m_is_server;
|
||||
alog_type& m_alog;
|
||||
elog_type& m_elog;
|
||||
lib::shared_ptr<alog_type> m_alog;
|
||||
lib::shared_ptr<elog_type> m_elog;
|
||||
|
||||
struct proxy_data {
|
||||
proxy_data() : timeout_proxy(config::timeout_proxy) {}
|
||||
@@ -1156,19 +1185,15 @@ private:
|
||||
|
||||
std::vector<lib::asio::const_buffer> m_bufs;
|
||||
|
||||
/// Detailed internal error code
|
||||
lib::asio::error_code m_tec;
|
||||
|
||||
// Handlers
|
||||
tcp_init_handler m_tcp_pre_init_handler;
|
||||
tcp_init_handler m_tcp_post_init_handler;
|
||||
|
||||
handler_allocator m_read_handler_allocator;
|
||||
handler_allocator m_write_handler_allocator;
|
||||
|
||||
read_handler m_read_handler;
|
||||
write_handler m_write_handler;
|
||||
init_handler m_init_handler;
|
||||
|
||||
async_read_handler m_async_read_handler;
|
||||
async_write_handler m_async_write_handler;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <websocketpp/uri.hpp>
|
||||
#include <websocketpp/logger/levels.hpp>
|
||||
|
||||
#include <websocketpp/common/asio.hpp>
|
||||
#include <websocketpp/common/functional.hpp>
|
||||
|
||||
#include <sstream>
|
||||
@@ -91,7 +92,7 @@ public:
|
||||
explicit endpoint()
|
||||
: m_io_service(NULL)
|
||||
, m_external_io_service(false)
|
||||
, m_listen_backlog(0)
|
||||
, m_listen_backlog(lib::asio::socket_base::max_connections)
|
||||
, m_reuse_addr(false)
|
||||
, m_state(UNINITIALIZED)
|
||||
{
|
||||
@@ -100,7 +101,11 @@ public:
|
||||
|
||||
~endpoint() {
|
||||
// clean up our io_service 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;
|
||||
}
|
||||
@@ -218,8 +223,15 @@ public:
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
void init_asio(lib::error_code & ec) {
|
||||
// Use a smart pointer until the call is successful and ownership has successfully been taken
|
||||
// Use a smart pointer until the call is successful and ownership has
|
||||
// successfully been taken. Use unique_ptr when available.
|
||||
// TODO: remove the use of auto_ptr when C++98/03 support is no longer
|
||||
// necessary.
|
||||
#ifdef _WEBSOCKETPP_CPP11_MEMORY_
|
||||
lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
|
||||
#else
|
||||
lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
|
||||
#endif
|
||||
init_asio(service.get(), ec);
|
||||
if( !ec ) service.release(); // Call was successful, transfer ownership
|
||||
m_external_io_service = false;
|
||||
@@ -233,8 +245,15 @@ public:
|
||||
* @see init_asio(io_service_ptr ptr)
|
||||
*/
|
||||
void init_asio() {
|
||||
// Use a smart pointer until the call is successful and ownership transferred
|
||||
// Use a smart pointer until the call is successful and ownership has
|
||||
// successfully been taken. Use unique_ptr when available.
|
||||
// TODO: remove the use of auto_ptr when C++98/03 support is no longer
|
||||
// necessary.
|
||||
#ifdef _WEBSOCKETPP_CPP11_MEMORY_
|
||||
lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
|
||||
#else
|
||||
lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
|
||||
#endif
|
||||
init_asio( service.get() );
|
||||
// If control got this far without an exception, then ownership has successfully been taken
|
||||
service.release();
|
||||
@@ -296,8 +315,10 @@ public:
|
||||
*
|
||||
* New values affect future calls to listen only.
|
||||
*
|
||||
* A value of zero will use the operating system default. This is the
|
||||
* default value.
|
||||
* The default value is specified as *::asio::socket_base::max_connections
|
||||
* which uses the operating system defined maximum queue length. Your OS
|
||||
* may restrict or silently lower this value. A value of zero may cause
|
||||
* all connections to be rejected.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
@@ -310,10 +331,13 @@ public:
|
||||
/// Sets whether to use the SO_REUSEADDR flag when opening listening sockets
|
||||
/**
|
||||
* Specifies whether or not to use the SO_REUSEADDR TCP socket option. What
|
||||
* this flag does depends on your operating system. Please consult operating
|
||||
* system documentation for more details.
|
||||
* this flag does depends on your operating system.
|
||||
*
|
||||
* New values affect future calls to listen only.
|
||||
* Please consult operating system documentation for more details. There
|
||||
* may be security consequences to enabling this option.
|
||||
*
|
||||
* New values affect future calls to listen only so set this value prior to
|
||||
* calling listen.
|
||||
*
|
||||
* The default is false.
|
||||
*
|
||||
@@ -339,6 +363,28 @@ public:
|
||||
lib::asio::io_service & get_io_service() {
|
||||
return *m_io_service;
|
||||
}
|
||||
|
||||
/// Get local TCP endpoint
|
||||
/**
|
||||
* Extracts the local endpoint from the acceptor. This represents the
|
||||
* address that WebSocket++ is listening on.
|
||||
*
|
||||
* Sets a bad_descriptor error if the acceptor is not currently listening
|
||||
* or otherwise unavailable.
|
||||
*
|
||||
* @since 0.7.0
|
||||
*
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
* @return The local endpoint
|
||||
*/
|
||||
lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) {
|
||||
if (m_acceptor) {
|
||||
return m_acceptor->local_endpoint(ec);
|
||||
} else {
|
||||
ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor);
|
||||
return lib::asio::ip::tcp::endpoint();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up endpoint for listening manually (exception free)
|
||||
/**
|
||||
@@ -703,7 +749,7 @@ public:
|
||||
void async_accept(transport_con_ptr tcon, accept_handler callback,
|
||||
lib::error_code & ec)
|
||||
{
|
||||
if (m_state != LISTENING) {
|
||||
if (m_state != LISTENING || !m_acceptor) {
|
||||
using websocketpp::error::make_error_code;
|
||||
ec = make_error_code(websocketpp::error::async_accept_not_listening);
|
||||
return;
|
||||
@@ -755,7 +801,7 @@ protected:
|
||||
* haven't been constructed yet, and cannot be used in the transport
|
||||
* destructor as they will have been destroyed by then.
|
||||
*/
|
||||
void init_logging(alog_type* a, elog_type* e) {
|
||||
void init_logging(const lib::shared_ptr<alog_type>& a, const lib::shared_ptr<elog_type>& e) {
|
||||
m_alog = a;
|
||||
m_elog = e;
|
||||
}
|
||||
@@ -1005,7 +1051,7 @@ protected:
|
||||
}
|
||||
|
||||
m_alog->write(log::alevel::devel,"TCP connect timed out");
|
||||
tcon->cancel_socket();
|
||||
tcon->cancel_socket_checked();
|
||||
callback(ret_ec);
|
||||
}
|
||||
|
||||
@@ -1093,8 +1139,8 @@ private:
|
||||
int m_listen_backlog;
|
||||
bool m_reuse_addr;
|
||||
|
||||
elog_type* m_elog;
|
||||
alog_type* m_alog;
|
||||
lib::shared_ptr<elog_type> m_elog;
|
||||
lib::shared_ptr<alog_type> m_alog;
|
||||
|
||||
// Transport state
|
||||
state m_state;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Peter Thorson. All rights reserved.
|
||||
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -28,6 +28,7 @@
|
||||
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
|
||||
#define WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
|
||||
|
||||
#include <websocketpp/common/asio.hpp>
|
||||
#include <websocketpp/common/memory.hpp>
|
||||
#include <websocketpp/common/functional.hpp>
|
||||
#include <websocketpp/common/system_error.hpp>
|
||||
@@ -63,6 +64,8 @@ namespace transport {
|
||||
namespace asio {
|
||||
namespace socket {
|
||||
|
||||
typedef lib::function<void(lib::asio::error_code const &)> shutdown_handler;
|
||||
|
||||
/**
|
||||
* The transport::asio::socket::* classes are a set of security/socket related
|
||||
* policies and support code for the ASIO transport types.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user