Compare commits
	
		
			34 Commits
		
	
	
		
			v11.4.3
			...
			bsergean-p
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2ddac836ad | ||
|  | 69e4bc4c05 | ||
|  | 3b66efbb6a | ||
|  | f29906c72f | ||
|  | 872f516ede | ||
|  | 014d43eb13 | ||
|  | d77067e50f | ||
|  | ed5b1a0895 | ||
|  | ef57e3a2b1 | ||
|  | 28832f8732 | ||
|  | 0dd284267a | ||
|  | a7019631b7 | ||
|  | 632ee31509 | ||
|  | 688af99747 | ||
|  | 397bb5d18a | ||
|  | f79c64ae97 | ||
|  | bc765e73a3 | ||
|  | dfa10df5ae | ||
|  | eb9a7bed76 | ||
|  | d20864d7d1 | ||
|  | f184a7adef | ||
|  | dc7b986e10 | ||
|  | 1e3560014f | ||
|  | 9157873f5b | ||
|  | aa2ca19895 | ||
|  | 6cc21f3658 | ||
|  | 679ce519dd | ||
|  | a5d4911a16 | ||
|  | b0fd119d14 | ||
|  | 472cf68c31 | ||
|  | 1e46466114 | ||
|  | 0b8b5608dc | ||
|  | 20a028e2ae | ||
|  | 8d7b557be6 | 
							
								
								
									
										19
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,19 +0,0 @@ | |||||||
| name: Mark stale issues and pull requests |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   schedule: |  | ||||||
|   - cron: "0 0 * * *" |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   stale: |  | ||||||
|  |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|  |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/stale@v1 |  | ||||||
|       with: |  | ||||||
|         repo-token: ${{ secrets.GITHUB_TOKEN }} |  | ||||||
|         stale-issue-message: 'Stale issue message' |  | ||||||
|         stale-pr-message: 'Stale pull request message' |  | ||||||
|         stale-issue-label: 'no-issue-activity' |  | ||||||
|         stale-pr-label: 'no-pr-activity' |  | ||||||
							
								
								
									
										27
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,27 +0,0 @@ | |||||||
| name: windows |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|   pull_request: |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   windows: |  | ||||||
|     runs-on: windows-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master |  | ||||||
|     - uses: seanmiddleditch/gha-setup-ninja@master |  | ||||||
|     - run: | |  | ||||||
|         mkdir build |  | ||||||
|         cd build |  | ||||||
|         cmake -GNinja -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=OFF -DBUILD_SHARED_LIBS=OFF .. |  | ||||||
|     - run: | |  | ||||||
|         cd build |  | ||||||
|         ninja |  | ||||||
|     - run: | |  | ||||||
|         cd build |  | ||||||
|         ninja test |  | ||||||
|  |  | ||||||
| #- run: ../build/test/ixwebsocket_unittest.exe |  | ||||||
| # working-directory: test |  | ||||||
							
								
								
									
										2
									
								
								.github/workflows/unittest_windows_gcc.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/unittest_windows_gcc.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,7 +11,7 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|     - uses: actions/checkout@v1 |     - uses: actions/checkout@v1 | ||||||
|     - uses: seanmiddleditch/gha-setup-ninja@master |     - uses: seanmiddleditch/gha-setup-ninja@master | ||||||
|     - uses: egor-tensin/setup-mingw@v2 |     - uses: bsergean/setup-mingw@d79ce405bac9edef3a1726ef00554a56f0bafe66 | ||||||
|     - run: | |     - run: | | ||||||
|         mkdir build |         mkdir build | ||||||
|         cd build |         cd build | ||||||
|   | |||||||
| @@ -6,11 +6,12 @@ | |||||||
| cmake_minimum_required(VERSION 3.4.1...3.17.2) | cmake_minimum_required(VERSION 3.4.1...3.17.2) | ||||||
| set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") | ||||||
|  |  | ||||||
| project(ixwebsocket C CXX) | project(ixwebsocket LANGUAGES C CXX VERSION 11.4.4) | ||||||
|  |  | ||||||
| set (CMAKE_CXX_STANDARD 11) | set (CMAKE_CXX_STANDARD 11) | ||||||
| set (CXX_STANDARD_REQUIRED ON) | set (CXX_STANDARD_REQUIRED ON) | ||||||
| set (CMAKE_CXX_EXTENSIONS OFF) | set (CMAKE_CXX_EXTENSIONS OFF) | ||||||
|  | set (CMAKE_EXPORT_COMPILE_COMMANDS yes) | ||||||
|  |  | ||||||
| option (BUILD_DEMO OFF) | option (BUILD_DEMO OFF) | ||||||
|  |  | ||||||
| @@ -135,6 +136,7 @@ if (USE_TLS) | |||||||
|     else() # default to OpenSSL on all other platforms |     else() # default to OpenSSL on all other platforms | ||||||
|       if (NOT USE_MBED_TLS) # Unless mbedtls is requested |       if (NOT USE_MBED_TLS) # Unless mbedtls is requested | ||||||
|         set(USE_OPEN_SSL ON) |         set(USE_OPEN_SSL ON) | ||||||
|  |         set(requires "openssl") | ||||||
|       endif() |       endif() | ||||||
|     endif() |     endif() | ||||||
|  |  | ||||||
| @@ -166,7 +168,7 @@ if(BUILD_SHARED_LIBS) | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     # Set library version |     # Set library version | ||||||
|     set_target_properties(ixwebsocket PROPERTIES VERSION 11.3.2) |     set_target_properties(ixwebsocket PROPERTIES VERSION ${CMAKE_PROJECT_VERSION}) | ||||||
|  |  | ||||||
| else() | else() | ||||||
|     # Static library |     # Static library | ||||||
| @@ -250,7 +252,7 @@ if (WIN32) | |||||||
|   endif() |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| if (UNIX) | if (UNIX AND NOT APPLE) | ||||||
|   set(THREADS_PREFER_PTHREAD_FLAG TRUE) |   set(THREADS_PREFER_PTHREAD_FLAG TRUE) | ||||||
|   find_package(Threads) |   find_package(Threads) | ||||||
|   target_link_libraries(ixwebsocket PRIVATE Threads::Threads) |   target_link_libraries(ixwebsocket PRIVATE Threads::Threads) | ||||||
| @@ -286,8 +288,12 @@ if (IXWEBSOCKET_INSTALL) | |||||||
|           PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/ |           PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/ | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY) |   configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY) | ||||||
|   install(FILES "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket") |   install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket") | ||||||
|  |    | ||||||
|  |   set(prefix ${CMAKE_INSTALL_PREFIX}) | ||||||
|  |   configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket.pc" @ONLY) | ||||||
|  |   install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") | ||||||
|  |  | ||||||
|   install(EXPORT ixwebsocket |   install(EXPORT ixwebsocket | ||||||
|           FILE ixwebsocket-targets.cmake |           FILE ixwebsocket-targets.cmake | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -99,14 +99,15 @@ Starting with the 11.0.8 release, IXWebSocket should be fully C++11 compatible. | |||||||
| If your company or project is using this library, feel free to open an issue or PR to amend this list. | If your company or project is using this library, feel free to open an issue or PR to amend this list. | ||||||
|  |  | ||||||
| - [Machine Zone](https://www.mz.com) | - [Machine Zone](https://www.mz.com) | ||||||
| - [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings. | - [Tokio](https://github.com/liz3/tokio), a discord library focused on audio playback with node bindings. | ||||||
| - [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework. | - [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework. | ||||||
| - [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod | - [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod | ||||||
| - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper | - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper (archived as of Oct 8, 2021) | ||||||
| - [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots | - [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots | ||||||
| - [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar | - [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar | ||||||
| - [Abaddon](https://github.com/uowuo/abaddon), An alternative Discord client made with C++/gtkmm  | - [Abaddon](https://github.com/uowuo/abaddon), An alternative Discord client made with C++/gtkmm  | ||||||
| - [NovaCoin](https://github.com/novacoin-project/novacoin), a hybrid scrypt PoW + PoS based cryptocurrency. | - [NovaCoin](https://github.com/novacoin-project/novacoin), a hybrid scrypt PoW + PoS based cryptocurrency. | ||||||
|  | - [Candy](https://github.com/lanthora/candy), A WebSocket and TUN based VPN for Linux  | ||||||
|  |  | ||||||
| ## Alternative libraries | ## Alternative libraries | ||||||
|  |  | ||||||
| @@ -114,8 +115,8 @@ There are plenty of great websocket libraries out there, which might work for yo | |||||||
|  |  | ||||||
| * [websocketpp](https://github.com/zaphoyd/websocketpp) - C++ | * [websocketpp](https://github.com/zaphoyd/websocketpp) - C++ | ||||||
| * [beast](https://github.com/boostorg/beast) - C++ | * [beast](https://github.com/boostorg/beast) - C++ | ||||||
|  | * [µWebSockets](https://github.com/uNetworking/uWebSockets) - C++ | ||||||
| * [libwebsockets](https://libwebsockets.org/) - C | * [libwebsockets](https://libwebsockets.org/) - C | ||||||
| * [µWebSockets](https://github.com/uNetworking/uWebSockets) - C |  | ||||||
| * [wslay](https://github.com/tatsuhiro-t/wslay) - C | * [wslay](https://github.com/tatsuhiro-t/wslay) - C | ||||||
|  |  | ||||||
| [uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv. | [uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv. | ||||||
| @@ -133,9 +134,7 @@ To check the performance of a websocket library, you can look at the [autoroute] | |||||||
| | Windows           | Disabled          | None              | [![Build2][5]][0] | | | Windows           | Disabled          | None              | [![Build2][5]][0] | | ||||||
| | UWP               | Disabled          | None              | [![Build2][6]][0] | | | UWP               | Disabled          | None              | [![Build2][6]][0] | | ||||||
| | Linux             | OpenSSL           | Address Sanitizer | [![Build2][7]][0] | | | Linux             | OpenSSL           | Address Sanitizer | [![Build2][7]][0] | | ||||||
| | Mingw             | Disabled          | None              | [![Build2][8]][0] | |  | ||||||
|  |  | ||||||
| * ASAN fails on Linux because of a known problem, we need a  |  | ||||||
| * Some tests are disabled on Windows/UWP because of a pathing problem | * Some tests are disabled on Windows/UWP because of a pathing problem | ||||||
| * TLS and ZLIB are disabled on Windows/UWP because enabling make the CI run takes a lot of time, for setting up vcpkg. | * TLS and ZLIB are disabled on Windows/UWP because enabling make the CI run takes a lot of time, for setting up vcpkg. | ||||||
|  |  | ||||||
| @@ -147,5 +146,4 @@ To check the performance of a websocket library, you can look at the [autoroute] | |||||||
| [5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg | [5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg | ||||||
| [6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg | [6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg | ||||||
| [7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg | [7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg | ||||||
| [8]: https://github.com/machinezone/IXWebSocket/workflows/unittest_windows_gcc/badge.svg |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ | |||||||
|  |  | ||||||
| All changes to this project will be documented in this file. | All changes to this project will be documented in this file. | ||||||
|  |  | ||||||
|  | ## [11.4.4] - 2023-06-05 | ||||||
|  |  | ||||||
| ## [11.4.3] - 2022-05-13 | ## [11.4.3] - 2022-05-13 | ||||||
|  |  | ||||||
| Set shorter thread names | Set shorter thread names | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ To use the installed package within a cmake project, use the following: | |||||||
|  # include headers |  # include headers | ||||||
|  include_directories(${IXWEBSOCKET_INCLUDE_DIR}) |  include_directories(${IXWEBSOCKET_INCLUDE_DIR}) | ||||||
|  # ... |  # ... | ||||||
|  target_link_libraries(${PROJECT_NAME} ... ${IXWEBSOCKET_LIBRARY}) # Cmake will automatically fail the generation if the lib was not found, i.e is set to NOTFOUNS |  target_link_libraries(${PROJECT_NAME} ... ${IXWEBSOCKET_LIBRARY}) # Cmake will automatically fail the generation if the lib was not found, i.e is set to NOTFOUND | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|   | |||||||
| @@ -301,7 +301,9 @@ This api was actually changed to take a weak_ptr<WebSocket> as the first argumen | |||||||
|  |  | ||||||
| // Run a server on localhost at a given port. | // Run a server on localhost at a given port. | ||||||
| // Bound host name, max connections and listen backlog can also be passed in as parameters. | // Bound host name, max connections and listen backlog can also be passed in as parameters. | ||||||
| ix::WebSocketServer server(port); | int port = 8008; | ||||||
|  | std::string host("127.0.0.1"); // If you need this server to be accessible on a different machine, use "0.0.0.0" | ||||||
|  | ix::WebSocketServer server(port, host); | ||||||
|  |  | ||||||
| server.setOnConnectionCallback( | server.setOnConnectionCallback( | ||||||
|     [&server](std::weak_ptr<WebSocket> webSocket, |     [&server](std::weak_ptr<WebSocket> webSocket, | ||||||
| @@ -384,7 +386,9 @@ The webSocket reference is guaranteed to be always valid ; by design the callbac | |||||||
|  |  | ||||||
| // Run a server on localhost at a given port. | // Run a server on localhost at a given port. | ||||||
| // Bound host name, max connections and listen backlog can also be passed in as parameters. | // Bound host name, max connections and listen backlog can also be passed in as parameters. | ||||||
| ix::WebSocketServer server(port); | int port = 8008; | ||||||
|  | std::string host("127.0.0.1"); // If you need this server to be accessible on a different machine, use "0.0.0.0" | ||||||
|  | ix::WebSocketServer server(port, host); | ||||||
|  |  | ||||||
| server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) { | server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) { | ||||||
|     // The ConnectionState object contains information about the connection, |     // The ConnectionState object contains information about the connection, | ||||||
| @@ -624,3 +628,5 @@ For a client, specifying `caFile` can be used if connecting to a server that use | |||||||
| For a server, specifying `caFile` implies that: | For a server, specifying `caFile` implies that: | ||||||
| 1. You require clients to present a certificate | 1. You require clients to present a certificate | ||||||
| 1. It must be signed by one of the trusted roots in the file | 1. It must be signed by one of the trusted roots in the file | ||||||
|  |  | ||||||
|  | By default, a destination's hostname is always validated against the certificate that it presents. To accept certificates with any hostname, set `ix::SocketTLSOptions::disable_hostname_validation` to `true`. | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								ixwebsocket.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ixwebsocket.pc.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | prefix=@prefix@ | ||||||
|  | exec_prefix=${prefix} | ||||||
|  | libdir=${exec_prefix}/lib | ||||||
|  | includedir=${prefix}/include | ||||||
|  |  | ||||||
|  | Name: ixwebsocket | ||||||
|  | Description: websocket and http client and server library, with TLS support and very few dependencies | ||||||
|  | Version: @CMAKE_PROJECT_VERSION@ | ||||||
|  | Libs: -L${libdir} -lixwebsocket | ||||||
|  | Cflags: -I${includedir} | ||||||
|  | Requires: @requires@ | ||||||
| @@ -25,6 +25,7 @@ | |||||||
|  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace macaron { | namespace macaron { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <stdint.h> | #include <cstdint> | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
|   | |||||||
| @@ -7,9 +7,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | #include <cstdint> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <stdint.h> |  | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
|   | |||||||
| @@ -23,12 +23,17 @@ | |||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <thread> | #include <thread> | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
| // mingw build quirks | // mingw build quirks | ||||||
| #if defined(_WIN32) && defined(__GNUC__) | #if defined(_WIN32) && defined(__GNUC__) | ||||||
|  | #ifndef AI_NUMERICSERV | ||||||
| #define AI_NUMERICSERV NI_NUMERICSERV | #define AI_NUMERICSERV NI_NUMERICSERV | ||||||
|  | #endif | ||||||
|  | #ifndef AI_ADDRCONFIG | ||||||
| #define AI_ADDRCONFIG LUP_ADDRCONFIG | #define AI_ADDRCONFIG LUP_ADDRCONFIG | ||||||
| #endif | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
| @@ -44,7 +49,7 @@ namespace ix | |||||||
|         ; |         ; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname, |     DNSLookup::AddrInfoPtr DNSLookup::getAddrInfo(const std::string& hostname, | ||||||
|                                             int port, |                                             int port, | ||||||
|                                             std::string& errMsg) |                                             std::string& errMsg) | ||||||
|     { |     { | ||||||
| @@ -63,10 +68,10 @@ namespace ix | |||||||
|             errMsg = gai_strerror(getaddrinfo_result); |             errMsg = gai_strerror(getaddrinfo_result); | ||||||
|             res = nullptr; |             res = nullptr; | ||||||
|         } |         } | ||||||
|         return res; |         return AddrInfoPtr{ res, freeaddrinfo }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct addrinfo* DNSLookup::resolve(std::string& errMsg, |     DNSLookup::AddrInfoPtr DNSLookup::resolve(std::string& errMsg, | ||||||
|                                         const CancellationRequest& isCancellationRequested, |                                         const CancellationRequest& isCancellationRequested, | ||||||
|                                         bool cancellable) |                                         bool cancellable) | ||||||
|     { |     { | ||||||
| @@ -74,12 +79,7 @@ namespace ix | |||||||
|                            : resolveUnCancellable(errMsg, isCancellationRequested); |                            : resolveUnCancellable(errMsg, isCancellationRequested); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void DNSLookup::release(struct addrinfo* addr) |     DNSLookup::AddrInfoPtr DNSLookup::resolveUnCancellable( | ||||||
|     { |  | ||||||
|         freeaddrinfo(addr); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct addrinfo* DNSLookup::resolveUnCancellable( |  | ||||||
|         std::string& errMsg, const CancellationRequest& isCancellationRequested) |         std::string& errMsg, const CancellationRequest& isCancellationRequested) | ||||||
|     { |     { | ||||||
|         errMsg = "no error"; |         errMsg = "no error"; | ||||||
| @@ -94,7 +94,7 @@ namespace ix | |||||||
|         return getAddrInfo(_hostname, _port, errMsg); |         return getAddrInfo(_hostname, _port, errMsg); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct addrinfo* DNSLookup::resolveCancellable( |     DNSLookup::AddrInfoPtr DNSLookup::resolveCancellable( | ||||||
|         std::string& errMsg, const CancellationRequest& isCancellationRequested) |         std::string& errMsg, const CancellationRequest& isCancellationRequested) | ||||||
|     { |     { | ||||||
|         errMsg = "no error"; |         errMsg = "no error"; | ||||||
| @@ -157,7 +157,7 @@ namespace ix | |||||||
|         // gone, so we use temporary variables (res) or we pass in by copy everything that |         // gone, so we use temporary variables (res) or we pass in by copy everything that | ||||||
|         // getAddrInfo needs to work. |         // getAddrInfo needs to work. | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         struct addrinfo* res = getAddrInfo(hostname, port, errMsg); |         auto res = getAddrInfo(hostname, port, errMsg); | ||||||
|  |  | ||||||
|         if (auto lock = self.lock()) |         if (auto lock = self.lock()) | ||||||
|         { |         { | ||||||
| @@ -181,13 +181,13 @@ namespace ix | |||||||
|         return _errMsg; |         return _errMsg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void DNSLookup::setRes(struct addrinfo* addr) |     void DNSLookup::setRes(DNSLookup::AddrInfoPtr addr) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_resMutex); |         std::lock_guard<std::mutex> lock(_resMutex); | ||||||
|         _res = addr; |         _res = std::move(addr); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct addrinfo* DNSLookup::getRes() |     DNSLookup::AddrInfoPtr DNSLookup::getRes() | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_resMutex); |         std::lock_guard<std::mutex> lock(_resMutex); | ||||||
|         return _res; |         return _res; | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
|  |  | ||||||
| #include "IXCancellationRequest.h" | #include "IXCancellationRequest.h" | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | #include <cstdint> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <set> | #include <set> | ||||||
| @@ -24,22 +25,21 @@ namespace ix | |||||||
|     class DNSLookup : public std::enable_shared_from_this<DNSLookup> |     class DNSLookup : public std::enable_shared_from_this<DNSLookup> | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|  |         using AddrInfoPtr = std::shared_ptr<addrinfo>; | ||||||
|         DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait); |         DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait); | ||||||
|         ~DNSLookup() = default; |         ~DNSLookup() = default; | ||||||
|  |  | ||||||
|         struct addrinfo* resolve(std::string& errMsg, |         AddrInfoPtr resolve(std::string& errMsg, | ||||||
|                                  const CancellationRequest& isCancellationRequested, |                                  const CancellationRequest& isCancellationRequested, | ||||||
|                                  bool cancellable = true); |                                  bool cancellable = true); | ||||||
|  |  | ||||||
|         void release(struct addrinfo* addr); |  | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         struct addrinfo* resolveCancellable(std::string& errMsg, |         AddrInfoPtr resolveCancellable(std::string& errMsg, | ||||||
|                                             const CancellationRequest& isCancellationRequested); |                                             const CancellationRequest& isCancellationRequested); | ||||||
|         struct addrinfo* resolveUnCancellable(std::string& errMsg, |         AddrInfoPtr resolveUnCancellable(std::string& errMsg, | ||||||
|                                               const CancellationRequest& isCancellationRequested); |                                               const CancellationRequest& isCancellationRequested); | ||||||
|  |  | ||||||
|         static struct addrinfo* getAddrInfo(const std::string& hostname, |         AddrInfoPtr getAddrInfo(const std::string& hostname, | ||||||
|                                             int port, |                                             int port, | ||||||
|                                             std::string& errMsg); |                                             std::string& errMsg); | ||||||
|  |  | ||||||
| @@ -48,15 +48,15 @@ namespace ix | |||||||
|         void setErrMsg(const std::string& errMsg); |         void setErrMsg(const std::string& errMsg); | ||||||
|         const std::string& getErrMsg(); |         const std::string& getErrMsg(); | ||||||
|  |  | ||||||
|         void setRes(struct addrinfo* addr); |         void setRes(AddrInfoPtr addr); | ||||||
|         struct addrinfo* getRes(); |         AddrInfoPtr getRes(); | ||||||
|  |  | ||||||
|         std::string _hostname; |         std::string _hostname; | ||||||
|         int _port; |         int _port; | ||||||
|         int64_t _wait; |         int64_t _wait; | ||||||
|         const static int64_t kDefaultWait; |         const static int64_t kDefaultWait; | ||||||
|  |  | ||||||
|         struct addrinfo* _res; |         AddrInfoPtr _res; | ||||||
|         std::mutex _resMutex; |         std::mutex _resMutex; | ||||||
|  |  | ||||||
|         std::string _errMsg; |         std::string _errMsg; | ||||||
|   | |||||||
| @@ -133,16 +133,20 @@ namespace ix | |||||||
|         if (headers.find("Content-Length") != headers.end()) |         if (headers.find("Content-Length") != headers.end()) | ||||||
|         { |         { | ||||||
|             int contentLength = 0; |             int contentLength = 0; | ||||||
|             try |  | ||||||
|             { |             { | ||||||
|                 contentLength = std::stoi(headers["Content-Length"]); |                 const char* p = headers["Content-Length"].c_str(); | ||||||
|  |                 char* p_end{}; | ||||||
|  |                 errno = 0; | ||||||
|  |                 long val = std::strtol(p, &p_end, 10); | ||||||
|  |                 if (p_end == p            // invalid argument | ||||||
|  |                     || errno == ERANGE    // out of range | ||||||
|  |                     || val < std::numeric_limits<int>::min() | ||||||
|  |                     || val > std::numeric_limits<int>::max()) { | ||||||
|  |                     return std::make_tuple( | ||||||
|  |                         false, "Error parsing HTTP Header 'Content-Length'", httpRequest); | ||||||
|  |                 } | ||||||
|  |                 contentLength = val; | ||||||
|             } |             } | ||||||
|             catch (const std::exception&) |  | ||||||
|             { |  | ||||||
|                 return std::make_tuple( |  | ||||||
|                     false, "Error parsing HTTP Header 'Content-Length'", httpRequest); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (contentLength < 0) |             if (contentLength < 0) | ||||||
|             { |             { | ||||||
|                 return std::make_tuple( |                 return std::make_tuple( | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
| #include "IXProgressCallback.h" | #include "IXProgressCallback.h" | ||||||
| #include "IXWebSocketHttpHeaders.h" | #include "IXWebSocketHttpHeaders.h" | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | #include <cstdint> | ||||||
| #include <tuple> | #include <tuple> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
| #include "IXUserAgent.h" | #include "IXUserAgent.h" | ||||||
| #include "IXWebSocketHttpHeaders.h" | #include "IXWebSocketHttpHeaders.h" | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  | #include <cstdint> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| #include <random> | #include <random> | ||||||
| @@ -139,8 +140,9 @@ namespace ix | |||||||
|  |  | ||||||
|         std::string protocol, host, path, query; |         std::string protocol, host, path, query; | ||||||
|         int port; |         int port; | ||||||
|  |         bool isProtocolDefaultPort; | ||||||
|  |  | ||||||
|         if (!UrlParser::parse(url, protocol, host, path, query, port)) |         if (!UrlParser::parse(url, protocol, host, path, query, port, isProtocolDefaultPort)) | ||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
|             ss << "Cannot parse url: " << url; |             ss << "Cannot parse url: " << url; | ||||||
| @@ -173,7 +175,12 @@ namespace ix | |||||||
|         // Build request string |         // Build request string | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << verb << " " << path << " HTTP/1.1\r\n"; |         ss << verb << " " << path << " HTTP/1.1\r\n"; | ||||||
|         ss << "Host: " << host << "\r\n"; |         ss << "Host: " << host; | ||||||
|  |         if (!isProtocolDefaultPort) | ||||||
|  |         { | ||||||
|  |             ss << ":" << port; | ||||||
|  |         } | ||||||
|  |         ss << "\r\n"; | ||||||
|  |  | ||||||
| #ifdef IXWEBSOCKET_USE_ZLIB | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         if (args->compress && !args->onChunkCallback) |         if (args->compress && !args->onChunkCallback) | ||||||
| @@ -202,6 +209,12 @@ namespace ix | |||||||
|             ss << "User-Agent: " << userAgent() << "\r\n"; |             ss << "User-Agent: " << userAgent() << "\r\n"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // Set an origin header if missing | ||||||
|  |         if (args->extraHeaders.find("Origin") == args->extraHeaders.end()) | ||||||
|  |         { | ||||||
|  |             ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (verb == kPost || verb == kPut || verb == kPatch || _forceBody) |         if (verb == kPost || verb == kPut || verb == kPatch || _forceBody) | ||||||
|         { |         { | ||||||
|             // Set request compression header |             // Set request compression header | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
| #include "IXNetSystem.h" | #include "IXNetSystem.h" | ||||||
| #include "IXSocketConnect.h" | #include "IXSocketConnect.h" | ||||||
| #include "IXUserAgent.h" | #include "IXUserAgent.h" | ||||||
|  | #include <cstdint> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <fstream> | #include <fstream> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| @@ -40,6 +41,29 @@ namespace | |||||||
|         auto vec = res.second; |         auto vec = res.second; | ||||||
|         return std::make_pair(res.first, std::string(vec.begin(), vec.end())); |         return std::make_pair(res.first, std::string(vec.begin(), vec.end())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::string response_head_file(const std::string& file_name){ | ||||||
|  |  | ||||||
|  |         if (std::string::npos != file_name.find(".html") || std::string::npos != file_name.find(".htm")) | ||||||
|  |             return "text/html"; | ||||||
|  |         else if (std::string::npos != file_name.find(".css")) | ||||||
|  |             return "text/css"; | ||||||
|  |         else if (std::string::npos != file_name.find(".js") || std::string::npos != file_name.find(".mjs")) | ||||||
|  |             return "application/x-javascript"; | ||||||
|  |         else if (std::string::npos != file_name.find(".ico")) | ||||||
|  |             return "image/x-icon"; | ||||||
|  |         else if (std::string::npos != file_name.find(".png")) | ||||||
|  |             return "image/png"; | ||||||
|  |         else if (std::string::npos != file_name.find(".jpg") || std::string::npos != file_name.find(".jpeg")) | ||||||
|  |             return "image/jpeg"; | ||||||
|  |         else if (std::string::npos != file_name.find(".gif")) | ||||||
|  |             return "image/gif"; | ||||||
|  |         else if (std::string::npos != file_name.find(".svg")) | ||||||
|  |             return "image/svg+xml"; | ||||||
|  |         else | ||||||
|  |             return "application/octet-stream"; | ||||||
|  |     } | ||||||
|  |  | ||||||
| } // namespace | } // namespace | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -51,28 +75,14 @@ namespace ix | |||||||
|                            int backlog, |                            int backlog, | ||||||
|                            size_t maxConnections, |                            size_t maxConnections, | ||||||
|                            int addressFamily, |                            int addressFamily, | ||||||
|                            int timeoutSecs) |                            int timeoutSecs, | ||||||
|         : SocketServer(port, host, backlog, maxConnections, addressFamily) |                            int handshakeTimeoutSecs) | ||||||
|         , _connectedClientsCount(0) |         : WebSocketServer(port, host, backlog, maxConnections, handshakeTimeoutSecs, addressFamily) | ||||||
|         , _timeoutSecs(timeoutSecs) |         , _timeoutSecs(timeoutSecs) | ||||||
|     { |     { | ||||||
|         setDefaultConnectionCallback(); |         setDefaultConnectionCallback(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     HttpServer::~HttpServer() |  | ||||||
|     { |  | ||||||
|         stop(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void HttpServer::stop() |  | ||||||
|     { |  | ||||||
|         stopAcceptingConnections(); |  | ||||||
|  |  | ||||||
|         // FIXME: cancelling / closing active clients ... |  | ||||||
|  |  | ||||||
|         SocketServer::stop(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void HttpServer::setOnConnectionCallback(const OnConnectionCallback& callback) |     void HttpServer::setOnConnectionCallback(const OnConnectionCallback& callback) | ||||||
|     { |     { | ||||||
|         _onConnectionCallback = callback; |         _onConnectionCallback = callback; | ||||||
| @@ -81,34 +91,35 @@ namespace ix | |||||||
|     void HttpServer::handleConnection(std::unique_ptr<Socket> socket, |     void HttpServer::handleConnection(std::unique_ptr<Socket> socket, | ||||||
|                                       std::shared_ptr<ConnectionState> connectionState) |                                       std::shared_ptr<ConnectionState> connectionState) | ||||||
|     { |     { | ||||||
|         _connectedClientsCount++; |  | ||||||
|  |  | ||||||
|         auto ret = Http::parseRequest(socket, _timeoutSecs); |         auto ret = Http::parseRequest(socket, _timeoutSecs); | ||||||
|         // FIXME: handle errors in parseRequest |         // FIXME: handle errors in parseRequest | ||||||
|  |  | ||||||
|         if (std::get<0>(ret)) |         if (std::get<0>(ret)) | ||||||
|         { |         { | ||||||
|             auto response = _onConnectionCallback(std::get<2>(ret), connectionState); |             auto request = std::get<2>(ret); | ||||||
|             if (!Http::sendResponse(response, socket)) |             std::shared_ptr<ix::HttpResponse> response; | ||||||
|  |             if (request->headers["Upgrade"] == "websocket") | ||||||
|             { |             { | ||||||
|                 logError("Cannot send response"); |                 WebSocketServer::handleUpgrade(std::move(socket), connectionState, request); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 auto response = _onConnectionCallback(request, connectionState); | ||||||
|  |                 if (!Http::sendResponse(response, socket)) | ||||||
|  |                 { | ||||||
|  |                     logError("Cannot send response"); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         connectionState->setTerminated(); |         connectionState->setTerminated(); | ||||||
|  |  | ||||||
|         _connectedClientsCount--; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     size_t HttpServer::getConnectedClientsCount() |  | ||||||
|     { |  | ||||||
|         return _connectedClientsCount; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void HttpServer::setDefaultConnectionCallback() |     void HttpServer::setDefaultConnectionCallback() | ||||||
|     { |     { | ||||||
|         setOnConnectionCallback( |         setOnConnectionCallback( | ||||||
|             [this](HttpRequestPtr request, |             [this](HttpRequestPtr request, | ||||||
|                    std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr { |                    std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr | ||||||
|  |             { | ||||||
|                 std::string uri(request->uri); |                 std::string uri(request->uri); | ||||||
|                 if (uri.empty() || uri == "/") |                 if (uri.empty() || uri == "/") | ||||||
|                 { |                 { | ||||||
| @@ -117,6 +128,7 @@ namespace ix | |||||||
|  |  | ||||||
|                 WebSocketHttpHeaders headers; |                 WebSocketHttpHeaders headers; | ||||||
|                 headers["Server"] = userAgent(); |                 headers["Server"] = userAgent(); | ||||||
|  |                 headers["Content-Type"] = response_head_file(uri); | ||||||
|  |  | ||||||
|                 std::string path("." + uri); |                 std::string path("." + uri); | ||||||
|                 auto res = readAsString(path); |                 auto res = readAsString(path); | ||||||
| @@ -136,6 +148,7 @@ namespace ix | |||||||
|                     content = gzipCompress(content); |                     content = gzipCompress(content); | ||||||
|                     headers["Content-Encoding"] = "gzip"; |                     headers["Content-Encoding"] = "gzip"; | ||||||
|                 } |                 } | ||||||
|  |                 headers["Accept-Encoding"] = "gzip"; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|                 // Log request |                 // Log request | ||||||
| @@ -149,11 +162,6 @@ namespace ix | |||||||
|                 // headers["Content-Type"] = "application/octet-stream"; |                 // headers["Content-Type"] = "application/octet-stream"; | ||||||
|                 headers["Accept-Ranges"] = "none"; |                 headers["Accept-Ranges"] = "none"; | ||||||
|  |  | ||||||
|                 for (auto&& it : request->headers) |  | ||||||
|                 { |  | ||||||
|                     headers[it.first] = it.second; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 return std::make_shared<HttpResponse>( |                 return std::make_shared<HttpResponse>( | ||||||
|                     200, "OK", HttpErrorCode::Ok, headers, content); |                     200, "OK", HttpErrorCode::Ok, headers, content); | ||||||
|             }); |             }); | ||||||
| @@ -165,9 +173,9 @@ namespace ix | |||||||
|         // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections |         // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections | ||||||
|         // |         // | ||||||
|         setOnConnectionCallback( |         setOnConnectionCallback( | ||||||
|             [this, |             [this, redirectUrl](HttpRequestPtr request, | ||||||
|              redirectUrl](HttpRequestPtr request, |                                 std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr | ||||||
|                           std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr { |             { | ||||||
|                 WebSocketHttpHeaders headers; |                 WebSocketHttpHeaders headers; | ||||||
|                 headers["Server"] = userAgent(); |                 headers["Server"] = userAgent(); | ||||||
|  |  | ||||||
| @@ -198,7 +206,8 @@ namespace ix | |||||||
|     { |     { | ||||||
|         setOnConnectionCallback( |         setOnConnectionCallback( | ||||||
|             [this](HttpRequestPtr request, |             [this](HttpRequestPtr request, | ||||||
|                    std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr { |                    std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr | ||||||
|  |             { | ||||||
|                 WebSocketHttpHeaders headers; |                 WebSocketHttpHeaders headers; | ||||||
|                 headers["Server"] = userAgent(); |                 headers["Server"] = userAgent(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXHttp.h" | #include "IXHttp.h" | ||||||
| #include "IXSocketServer.h" |  | ||||||
| #include "IXWebSocket.h" | #include "IXWebSocket.h" | ||||||
|  | #include "IXWebSocketServer.h" | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| @@ -19,7 +19,7 @@ | |||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     class HttpServer final : public SocketServer |     class HttpServer final : public WebSocketServer | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|         using OnConnectionCallback = |         using OnConnectionCallback = | ||||||
| @@ -30,9 +30,8 @@ namespace ix | |||||||
|                    int backlog = SocketServer::kDefaultTcpBacklog, |                    int backlog = SocketServer::kDefaultTcpBacklog, | ||||||
|                    size_t maxConnections = SocketServer::kDefaultMaxConnections, |                    size_t maxConnections = SocketServer::kDefaultMaxConnections, | ||||||
|                    int addressFamily = SocketServer::kDefaultAddressFamily, |                    int addressFamily = SocketServer::kDefaultAddressFamily, | ||||||
|                    int timeoutSecs = HttpServer::kDefaultTimeoutSecs); |                    int timeoutSecs = HttpServer::kDefaultTimeoutSecs, | ||||||
|         virtual ~HttpServer(); |                    int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs); | ||||||
|         virtual void stop() final; |  | ||||||
|  |  | ||||||
|         void setOnConnectionCallback(const OnConnectionCallback& callback); |         void setOnConnectionCallback(const OnConnectionCallback& callback); | ||||||
|  |  | ||||||
| @@ -41,10 +40,10 @@ namespace ix | |||||||
|         void makeDebugServer(); |         void makeDebugServer(); | ||||||
|  |  | ||||||
|         int getTimeoutSecs(); |         int getTimeoutSecs(); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         // Member variables |         // Member variables | ||||||
|         OnConnectionCallback _onConnectionCallback; |         OnConnectionCallback _onConnectionCallback; | ||||||
|         std::atomic<int> _connectedClientsCount; |  | ||||||
|  |  | ||||||
|         const static int kDefaultTimeoutSecs; |         const static int kDefaultTimeoutSecs; | ||||||
|         int _timeoutSecs; |         int _timeoutSecs; | ||||||
| @@ -52,7 +51,6 @@ namespace ix | |||||||
|         // Methods |         // Methods | ||||||
|         virtual void handleConnection(std::unique_ptr<Socket>, |         virtual void handleConnection(std::unique_ptr<Socket>, | ||||||
|                                       std::shared_ptr<ConnectionState> connectionState) final; |                                       std::shared_ptr<ConnectionState> connectionState) final; | ||||||
|         virtual size_t getConnectedClientsCount() final; |  | ||||||
|  |  | ||||||
|         void setDefaultConnectionCallback(); |         void setDefaultConnectionCallback(); | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -69,7 +69,7 @@ namespace ix | |||||||
|             { |             { | ||||||
|                 // We must deselect the networkevents from the socket event. Otherwise the |                 // We must deselect the networkevents from the socket event. Otherwise the | ||||||
|                 // socket will report states that aren't there. |                 // socket will report states that aren't there. | ||||||
|                 if (_fd != nullptr && _fd->fd != -1) |                 if (_fd != nullptr && (int)_fd->fd != -1) | ||||||
|                     WSAEventSelect(_fd->fd, _event, 0); |                     WSAEventSelect(_fd->fd, _event, 0); | ||||||
|                 WSACloseEvent(_event); |                 WSACloseEvent(_event); | ||||||
|             } |             } | ||||||
| @@ -171,7 +171,7 @@ namespace ix | |||||||
|             int count = 0; |             int count = 0; | ||||||
|             // WSAWaitForMultipleEvents returns the index of the first signaled event. And to emulate WSAPoll() |             // WSAWaitForMultipleEvents returns the index of the first signaled event. And to emulate WSAPoll() | ||||||
|             // all the signaled events must be processed. |             // all the signaled events must be processed. | ||||||
|             while (socketIndex < socketEvents.size()) |             while (socketIndex < (int)socketEvents.size()) | ||||||
|             { |             { | ||||||
|                 struct pollfd* fd = socketEvents[socketIndex]; |                 struct pollfd* fd = socketEvents[socketIndex]; | ||||||
|  |  | ||||||
| @@ -345,7 +345,7 @@ namespace ix | |||||||
|                     buf[best] = buf[best + 1] = ':'; |                     buf[best] = buf[best + 1] = ':'; | ||||||
|                     memmove(buf + best + 2, buf + best + max, i - best - max + 1); |                     memmove(buf + best + 2, buf + best + max, i - best - max + 1); | ||||||
|                 } |                 } | ||||||
|                 if (strlen(buf) < l) |                 if (strlen(buf) < (size_t)l) | ||||||
|                 { |                 { | ||||||
|                     strcpy(s, buf); |                     strcpy(s, buf); | ||||||
|                     return s; |                     return s; | ||||||
|   | |||||||
| @@ -6,6 +6,12 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
|  |  | ||||||
|  | #ifdef __FreeBSD__ | ||||||
|  | #include <sys/types.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|  |  | ||||||
| #ifndef WIN32_LEAN_AND_MEAN | #ifndef WIN32_LEAN_AND_MEAN | ||||||
|   | |||||||
| @@ -6,8 +6,8 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <stdint.h> |  | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
|   | |||||||
| @@ -5,8 +5,8 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXSelectInterrupt.h" | #include "IXSelectInterrupt.h" | ||||||
|  | #include <cstdint> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <stdint.h> |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <deque> | #include <deque> | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXSelectInterrupt.h" | #include "IXSelectInterrupt.h" | ||||||
|  | #include <cstdint> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <string> | #include <string> | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ | |||||||
| #include <array> | #include <array> | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | #include <cstdint> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|   | |||||||
| @@ -205,7 +205,9 @@ namespace ix | |||||||
|                 _sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket); |                 _sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket); | ||||||
|             SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); |             SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); | ||||||
|             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); |             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); | ||||||
|             SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); |  | ||||||
|  |             if (!_tlsOptions.disable_hostname_validation) | ||||||
|  |                 SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); | ||||||
|  |  | ||||||
|             if (_tlsOptions.isPeerVerifyDisabled()) |             if (_tlsOptions.isPeerVerifyDisabled()) | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -102,7 +102,7 @@ namespace ix | |||||||
|         // First do DNS resolution |         // First do DNS resolution | ||||||
|         // |         // | ||||||
|         auto dnsLookup = std::make_shared<DNSLookup>(hostname, port); |         auto dnsLookup = std::make_shared<DNSLookup>(hostname, port); | ||||||
|         struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested); |         auto res = dnsLookup->resolve(errMsg, isCancellationRequested); | ||||||
|         if (res == nullptr) |         if (res == nullptr) | ||||||
|         { |         { | ||||||
|             return -1; |             return -1; | ||||||
| @@ -112,7 +112,7 @@ namespace ix | |||||||
|  |  | ||||||
|         // iterate through the records to find a working peer |         // iterate through the records to find a working peer | ||||||
|         struct addrinfo* address; |         struct addrinfo* address; | ||||||
|         for (address = res; address != nullptr; address = address->ai_next) |         for (address = res.get(); address != nullptr; address = address->ai_next) | ||||||
|         { |         { | ||||||
|             // |             // | ||||||
|             // Second try to connect to the remote host |             // Second try to connect to the remote host | ||||||
| @@ -124,7 +124,6 @@ namespace ix | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         freeaddrinfo(res); |  | ||||||
|         return sockfd; |         return sockfd; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
| #include "IXNetSystem.h" | #include "IXNetSystem.h" | ||||||
| #include "IXSocket.h" | #include "IXSocket.h" | ||||||
| #include "IXSocketConnect.h" | #include "IXSocketConnect.h" | ||||||
|  | #include <cstdint> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| @@ -195,10 +196,13 @@ namespace ix | |||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0) |         if (!_tlsOptions.disable_hostname_validation) | ||||||
|         { |         { | ||||||
|             errMsg = "SNI setup failed"; |             if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0) | ||||||
|             return false; |             { | ||||||
|  |                 errMsg = "SNI setup failed"; | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ | |||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| // For manipulating the certificate store | // For manipulating the certificate store | ||||||
|  | #include <windows.h> | ||||||
| #include <wincrypt.h> | #include <wincrypt.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -49,7 +50,7 @@ namespace | |||||||
|         X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl); |         X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl); | ||||||
|  |  | ||||||
|         int certificateCount = 0; |         int certificateCount = 0; | ||||||
|         while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator)) |         while ((certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator))) | ||||||
|         { |         { | ||||||
|             X509* x509 = d2i_X509(NULL, |             X509* x509 = d2i_X509(NULL, | ||||||
|                                   (const unsigned char**) &certificateIterator->pbCertEncoded, |                                   (const unsigned char**) &certificateIterator->pbCertEncoded, | ||||||
| @@ -293,15 +294,25 @@ namespace ix | |||||||
|      */ |      */ | ||||||
|     bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern) |     bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern) | ||||||
|     { |     { | ||||||
|  | #if OPENSSL_VERSION_NUMBER >= 0x10100000L | ||||||
|  |         return true; | ||||||
|  | #else | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|         return PathMatchSpecA(host.c_str(), pattern); |         return PathMatchSpecA(host.c_str(), pattern); | ||||||
| #else | #else | ||||||
|         return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; |         return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool SocketOpenSSL::openSSLCheckServerCert(SSL* ssl, |     bool SocketOpenSSL::openSSLCheckServerCert(SSL* ssl, | ||||||
|  | #if OPENSSL_VERSION_NUMBER < 0x10100000L | ||||||
|                                                const std::string& hostname, |                                                const std::string& hostname, | ||||||
|  | #else | ||||||
|  |                                                const std::string& /* hostname */, | ||||||
|  | #endif | ||||||
|                                                std::string& errMsg) |                                                std::string& errMsg) | ||||||
|     { |     { | ||||||
|         X509* server_cert = SSL_get_peer_certificate(ssl); |         X509* server_cert = SSL_get_peer_certificate(ssl); | ||||||
| @@ -390,6 +401,11 @@ namespace ix | |||||||
|             int connect_result = SSL_connect(_ssl_connection); |             int connect_result = SSL_connect(_ssl_connection); | ||||||
|             if (connect_result == 1) |             if (connect_result == 1) | ||||||
|             { |             { | ||||||
|  |                 if (_tlsOptions.disable_hostname_validation) | ||||||
|  |                 { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 return openSSLCheckServerCert(_ssl_connection, host, errMsg); |                 return openSSLCheckServerCert(_ssl_connection, host, errMsg); | ||||||
|             } |             } | ||||||
|             int reason = SSL_get_error(_ssl_connection, connect_result); |             int reason = SSL_get_error(_ssl_connection, connect_result); | ||||||
| @@ -754,8 +770,11 @@ namespace ix | |||||||
|             // (The docs say that this should work from 1.0.2, and is the default from |             // (The docs say that this should work from 1.0.2, and is the default from | ||||||
|             // 1.1.0, but it does not. To be on the safe side, the manual test |             // 1.1.0, but it does not. To be on the safe side, the manual test | ||||||
|             // below is enabled for all versions prior to 1.1.0.) |             // below is enabled for all versions prior to 1.1.0.) | ||||||
|             X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection); |             if (!_tlsOptions.disable_hostname_validation) | ||||||
|             X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size()); |             { | ||||||
|  |                 X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection); | ||||||
|  |                 X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size()); | ||||||
|  |             } | ||||||
| #endif | #endif | ||||||
|             handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested); |             handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -219,6 +219,10 @@ namespace ix | |||||||
|         if (_gcThread.joinable()) |         if (_gcThread.joinable()) | ||||||
|         { |         { | ||||||
|             _stopGc = true; |             _stopGc = true; | ||||||
|  |             { | ||||||
|  |                 std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC }; | ||||||
|  |                 _canContinueGC = true; | ||||||
|  |             } | ||||||
|             _conditionVariableGC.notify_one(); |             _conditionVariableGC.notify_one(); | ||||||
|             _gcThread.join(); |             _gcThread.join(); | ||||||
|             _stopGc = false; |             _stopGc = false; | ||||||
| @@ -451,7 +455,10 @@ namespace ix | |||||||
|             if (!_stopGc) |             if (!_stopGc) | ||||||
|             { |             { | ||||||
|                 std::unique_lock<std::mutex> lock(_conditionVariableMutexGC); |                 std::unique_lock<std::mutex> lock(_conditionVariableMutexGC); | ||||||
|                 _conditionVariableGC.wait(lock); |                 if(!_canContinueGC) { | ||||||
|  |                     _conditionVariableGC.wait(lock, [this]{ return _canContinueGC; }); | ||||||
|  |                 } | ||||||
|  |                 _canContinueGC = false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -465,6 +472,10 @@ namespace ix | |||||||
|     { |     { | ||||||
|         // a connection got terminated, we can run the connection thread GC, |         // a connection got terminated, we can run the connection thread GC, | ||||||
|         // so wake up the thread responsible for that |         // so wake up the thread responsible for that | ||||||
|  |         { | ||||||
|  |             std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC }; | ||||||
|  |             _canContinueGC = true; | ||||||
|  |         } | ||||||
|         _conditionVariableGC.notify_one(); |         _conditionVariableGC.notify_one(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -126,5 +126,6 @@ namespace ix | |||||||
|         // as a connection |         // as a connection | ||||||
|         std::condition_variable _conditionVariableGC; |         std::condition_variable _conditionVariableGC; | ||||||
|         std::mutex _conditionVariableMutexGC; |         std::mutex _conditionVariableMutexGC; | ||||||
|  |         bool _canContinueGC{ false }; | ||||||
|     }; |     }; | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -33,6 +33,9 @@ namespace ix | |||||||
|         // whether tls is enabled, used for server code |         // whether tls is enabled, used for server code | ||||||
|         bool tls = false; |         bool tls = false; | ||||||
|  |  | ||||||
|  |         // whether to skip validating the peer's hostname against the certificate presented | ||||||
|  |         bool disable_hostname_validation = false; | ||||||
|  |  | ||||||
|         bool hasCertAndKey() const; |         bool hasCertAndKey() const; | ||||||
|  |  | ||||||
|         bool isUsingSystemDefaults() const; |         bool isUsingSystemDefaults() const; | ||||||
|   | |||||||
| @@ -333,6 +333,19 @@ namespace | |||||||
|  |  | ||||||
|         return Result; |         return Result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     int getProtocolPort(const std::string& protocol) | ||||||
|  |     { | ||||||
|  |         if (protocol == "ws" || protocol == "http") | ||||||
|  |         { | ||||||
|  |             return 80; | ||||||
|  |         } | ||||||
|  |         else if (protocol == "wss" || protocol == "https") | ||||||
|  |         { | ||||||
|  |             return 443; | ||||||
|  |         } | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
| } // namespace | } // namespace | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -343,6 +356,18 @@ namespace ix | |||||||
|                           std::string& path, |                           std::string& path, | ||||||
|                           std::string& query, |                           std::string& query, | ||||||
|                           int& port) |                           int& port) | ||||||
|  |     { | ||||||
|  |         bool isProtocolDefaultPort; | ||||||
|  |         return parse(url, protocol, host, path, query, port, isProtocolDefaultPort); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool UrlParser::parse(const std::string& url, | ||||||
|  |                               std::string& protocol, | ||||||
|  |                               std::string& host, | ||||||
|  |                               std::string& path, | ||||||
|  |                               std::string& query, | ||||||
|  |                               int& port, | ||||||
|  |                               bool& isProtocolDefaultPort) | ||||||
|     { |     { | ||||||
|         clParseURL res = clParseURL::ParseURL(url); |         clParseURL res = clParseURL::ParseURL(url); | ||||||
|  |  | ||||||
| @@ -356,23 +381,12 @@ namespace ix | |||||||
|         path = res.m_Path; |         path = res.m_Path; | ||||||
|         query = res.m_Query; |         query = res.m_Query; | ||||||
|  |  | ||||||
|  |         const auto protocolPort = getProtocolPort(protocol); | ||||||
|         if (!res.GetPort(&port)) |         if (!res.GetPort(&port)) | ||||||
|         { |         { | ||||||
|             if (protocol == "ws" || protocol == "http") |             port = protocolPort; | ||||||
|             { |  | ||||||
|                 port = 80; |  | ||||||
|             } |  | ||||||
|             else if (protocol == "wss" || protocol == "https") |  | ||||||
|             { |  | ||||||
|                 port = 443; |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 // Invalid protocol. Should be caught by regex check |  | ||||||
|                 // but this missing branch trigger cpplint linter. |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|  |         isProtocolDefaultPort = port == protocolPort; | ||||||
|  |  | ||||||
|         if (path.empty()) |         if (path.empty()) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -19,5 +19,13 @@ namespace ix | |||||||
|                           std::string& path, |                           std::string& path, | ||||||
|                           std::string& query, |                           std::string& query, | ||||||
|                           int& port); |                           int& port); | ||||||
|  |  | ||||||
|  |         static bool parse(const std::string& url, | ||||||
|  |                           std::string& protocol, | ||||||
|  |                           std::string& host, | ||||||
|  |                           std::string& path, | ||||||
|  |                           std::string& query, | ||||||
|  |                           int& port, | ||||||
|  |                           bool& isProtocolDefaultPort); | ||||||
|     }; |     }; | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ | |||||||
|  |  | ||||||
| #include "IXUuid.h" | #include "IXUuid.h" | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| #include <random> | #include <random> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ | |||||||
| #include "IXWebSocketHandshake.h" | #include "IXWebSocketHandshake.h" | ||||||
| #include <cassert> | #include <cassert> | ||||||
| #include <cmath> | #include <cmath> | ||||||
|  | #include <cstdint> | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace | namespace | ||||||
| @@ -39,9 +40,12 @@ namespace ix | |||||||
|         , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) |         , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) | ||||||
|         , _enablePong(kDefaultEnablePong) |         , _enablePong(kDefaultEnablePong) | ||||||
|         , _pingIntervalSecs(kDefaultPingIntervalSecs) |         , _pingIntervalSecs(kDefaultPingIntervalSecs) | ||||||
|  |         , _pingType(SendMessageKind::Ping) | ||||||
|  |         , _autoThreadName(true) | ||||||
|     { |     { | ||||||
|         _ws.setOnCloseCallback( |         _ws.setOnCloseCallback( | ||||||
|             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) { |             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) | ||||||
|  |             { | ||||||
|                 _onMessageCallback( |                 _onMessageCallback( | ||||||
|                     ix::make_unique<WebSocketMessage>(WebSocketMessageType::Close, |                     ix::make_unique<WebSocketMessage>(WebSocketMessageType::Close, | ||||||
|                                                       emptyMsg, |                                                       emptyMsg, | ||||||
| @@ -100,6 +104,17 @@ namespace ix | |||||||
|         return _perMessageDeflateOptions; |         return _perMessageDeflateOptions; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void WebSocket::setPingMessage(const std::string& sendMessage, SendMessageKind pingType) | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|  |         _pingMessage = sendMessage; | ||||||
|  |         _ws.setPingMessage(_pingMessage, pingType); | ||||||
|  |     } | ||||||
|  |     const std::string WebSocket::getPingMessage() const | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|  |         return _pingMessage; | ||||||
|  |     } | ||||||
|     void WebSocket::setPingInterval(int pingIntervalSecs) |     void WebSocket::setPingInterval(int pingIntervalSecs) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_configMutex); |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
| @@ -232,7 +247,7 @@ namespace ix | |||||||
|         if (_pingIntervalSecs > 0) |         if (_pingIntervalSecs > 0) | ||||||
|         { |         { | ||||||
|             // Send a heart beat right away |             // Send a heart beat right away | ||||||
|             _ws.sendHeartBeat(); |             _ws.sendHeartBeat(_pingType); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return status; |         return status; | ||||||
| @@ -240,7 +255,8 @@ namespace ix | |||||||
|  |  | ||||||
|     WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, |     WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, | ||||||
|                                                    int timeoutSecs, |                                                    int timeoutSecs, | ||||||
|                                                    bool enablePerMessageDeflate) |                                                    bool enablePerMessageDeflate, | ||||||
|  |                                                    HttpRequestPtr request) | ||||||
|     { |     { | ||||||
|         { |         { | ||||||
|             std::lock_guard<std::mutex> lock(_configMutex); |             std::lock_guard<std::mutex> lock(_configMutex); | ||||||
| @@ -249,7 +265,7 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         WebSocketInitResult status = |         WebSocketInitResult status = | ||||||
|             _ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate); |             _ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate, request); | ||||||
|         if (!status.success) |         if (!status.success) | ||||||
|         { |         { | ||||||
|             return status; |             return status; | ||||||
| @@ -266,7 +282,7 @@ namespace ix | |||||||
|         if (_pingIntervalSecs > 0) |         if (_pingIntervalSecs > 0) | ||||||
|         { |         { | ||||||
|             // Send a heart beat right away |             // Send a heart beat right away | ||||||
|             _ws.sendHeartBeat(); |             _ws.sendHeartBeat(_pingType); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return status; |         return status; | ||||||
| @@ -355,7 +371,10 @@ namespace ix | |||||||
|  |  | ||||||
|     void WebSocket::run() |     void WebSocket::run() | ||||||
|     { |     { | ||||||
|         setThreadName(getUrl()); |         if (_autoThreadName) | ||||||
|  |         { | ||||||
|  |             setThreadName(getUrl()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         bool firstConnectionAttempt = true; |         bool firstConnectionAttempt = true; | ||||||
|  |  | ||||||
| @@ -384,8 +403,9 @@ namespace ix | |||||||
|                 [this](const std::string& msg, |                 [this](const std::string& msg, | ||||||
|                        size_t wireSize, |                        size_t wireSize, | ||||||
|                        bool decompressionError, |                        bool decompressionError, | ||||||
|                        WebSocketTransport::MessageKind messageKind) { |                        WebSocketTransport::MessageKind messageKind) | ||||||
|                     WebSocketMessageType webSocketMessageType{WebSocketMessageType::Error}; |                 { | ||||||
|  |                     WebSocketMessageType webSocketMessageType {WebSocketMessageType::Error}; | ||||||
|                     switch (messageKind) |                     switch (messageKind) | ||||||
|                     { |                     { | ||||||
|                         case WebSocketTransport::MessageKind::MSG_TEXT: |                         case WebSocketTransport::MessageKind::MSG_TEXT: | ||||||
| @@ -503,13 +523,13 @@ namespace ix | |||||||
|         return sendMessage(text, SendMessageKind::Text, onProgressCallback); |         return sendMessage(text, SendMessageKind::Text, onProgressCallback); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketSendInfo WebSocket::ping(const std::string& text) |     WebSocketSendInfo WebSocket::ping(const std::string& text, SendMessageKind pingType) | ||||||
|     { |     { | ||||||
|         // Standard limit ping message size |         // Standard limit ping message size | ||||||
|         constexpr size_t pingMaxPayloadSize = 125; |         constexpr size_t pingMaxPayloadSize = 125; | ||||||
|         if (text.size() > pingMaxPayloadSize) return WebSocketSendInfo(false); |         if (text.size() > pingMaxPayloadSize) return WebSocketSendInfo(false); | ||||||
|  |  | ||||||
|         return sendMessage(text, SendMessageKind::Ping); |         return sendMessage(text, pingType); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketSendInfo WebSocket::sendMessage(const IXWebSocketSendData& message, |     WebSocketSendInfo WebSocket::sendMessage(const IXWebSocketSendData& message, | ||||||
| @@ -611,4 +631,9 @@ namespace ix | |||||||
|         std::lock_guard<std::mutex> lock(_configMutex); |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|         return _subProtocols; |         return _subProtocols; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void WebSocket::setAutoThreadName(bool enabled) | ||||||
|  |     { | ||||||
|  |         _autoThreadName = enabled; | ||||||
|  |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -16,11 +16,12 @@ | |||||||
| #include "IXWebSocketHttpHeaders.h" | #include "IXWebSocketHttpHeaders.h" | ||||||
| #include "IXWebSocketMessage.h" | #include "IXWebSocketMessage.h" | ||||||
| #include "IXWebSocketPerMessageDeflateOptions.h" | #include "IXWebSocketPerMessageDeflateOptions.h" | ||||||
| #include "IXWebSocketSendInfo.h" |  | ||||||
| #include "IXWebSocketSendData.h" | #include "IXWebSocketSendData.h" | ||||||
|  | #include "IXWebSocketSendInfo.h" | ||||||
| #include "IXWebSocketTransport.h" | #include "IXWebSocketTransport.h" | ||||||
| #include <atomic> | #include <atomic> | ||||||
| #include <condition_variable> | #include <condition_variable> | ||||||
|  | #include <cstdint> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <string> | #include <string> | ||||||
| #include <thread> | #include <thread> | ||||||
| @@ -53,6 +54,8 @@ namespace ix | |||||||
|         void setPerMessageDeflateOptions( |         void setPerMessageDeflateOptions( | ||||||
|             const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions); |             const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions); | ||||||
|         void setTLSOptions(const SocketTLSOptions& socketTLSOptions); |         void setTLSOptions(const SocketTLSOptions& socketTLSOptions); | ||||||
|  |         void setPingMessage(const std::string& sendMessage, | ||||||
|  |                             SendMessageKind pingType = SendMessageKind::Ping); | ||||||
|         void setPingInterval(int pingIntervalSecs); |         void setPingInterval(int pingIntervalSecs); | ||||||
|         void enablePong(); |         void enablePong(); | ||||||
|         void disablePong(); |         void disablePong(); | ||||||
| @@ -88,7 +91,7 @@ namespace ix | |||||||
|                                        const OnProgressCallback& onProgressCallback = nullptr); |                                        const OnProgressCallback& onProgressCallback = nullptr); | ||||||
|         WebSocketSendInfo sendText(const std::string& text, |         WebSocketSendInfo sendText(const std::string& text, | ||||||
|                                    const OnProgressCallback& onProgressCallback = nullptr); |                                    const OnProgressCallback& onProgressCallback = nullptr); | ||||||
|         WebSocketSendInfo ping(const std::string& text); |         WebSocketSendInfo ping(const std::string& text,SendMessageKind pingType = SendMessageKind::Ping); | ||||||
|  |  | ||||||
|         void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode, |         void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode, | ||||||
|                    const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage); |                    const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage); | ||||||
| @@ -103,6 +106,7 @@ namespace ix | |||||||
|  |  | ||||||
|         const std::string getUrl() const; |         const std::string getUrl() const; | ||||||
|         const WebSocketPerMessageDeflateOptions getPerMessageDeflateOptions() const; |         const WebSocketPerMessageDeflateOptions getPerMessageDeflateOptions() const; | ||||||
|  |         const std::string getPingMessage() const; | ||||||
|         int getPingInterval() const; |         int getPingInterval() const; | ||||||
|         size_t bufferedAmount() const; |         size_t bufferedAmount() const; | ||||||
|  |  | ||||||
| @@ -115,6 +119,8 @@ namespace ix | |||||||
|         uint32_t getMinWaitBetweenReconnectionRetries() const; |         uint32_t getMinWaitBetweenReconnectionRetries() const; | ||||||
|         const std::vector<std::string>& getSubProtocols(); |         const std::vector<std::string>& getSubProtocols(); | ||||||
|  |  | ||||||
|  |         void setAutoThreadName(bool enabled); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         WebSocketSendInfo sendMessage(const IXWebSocketSendData& message, |         WebSocketSendInfo sendMessage(const IXWebSocketSendData& message, | ||||||
|                                       SendMessageKind sendMessageKind, |                                       SendMessageKind sendMessageKind, | ||||||
| @@ -128,7 +134,8 @@ namespace ix | |||||||
|         // Server |         // Server | ||||||
|         WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, |         WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, | ||||||
|                                             int timeoutSecs, |                                             int timeoutSecs, | ||||||
|                                             bool enablePerMessageDeflate); |                                             bool enablePerMessageDeflate, | ||||||
|  |                                             HttpRequestPtr request = nullptr); | ||||||
|  |  | ||||||
|         WebSocketTransport _ws; |         WebSocketTransport _ws; | ||||||
|  |  | ||||||
| @@ -169,12 +176,17 @@ namespace ix | |||||||
|         // Optional ping and pong timeout |         // Optional ping and pong timeout | ||||||
|         int _pingIntervalSecs; |         int _pingIntervalSecs; | ||||||
|         int _pingTimeoutSecs; |         int _pingTimeoutSecs; | ||||||
|  |         std::string _pingMessage; | ||||||
|  |         SendMessageKind _pingType; | ||||||
|         static const int kDefaultPingIntervalSecs; |         static const int kDefaultPingIntervalSecs; | ||||||
|         static const int kDefaultPingTimeoutSecs; |         static const int kDefaultPingTimeoutSecs; | ||||||
|  |  | ||||||
|         // Subprotocols |         // Subprotocols | ||||||
|         std::vector<std::string> _subProtocols; |         std::vector<std::string> _subProtocols; | ||||||
|  |  | ||||||
|  |         // enable or disable auto set thread name | ||||||
|  |         bool _autoThreadName; | ||||||
|  |  | ||||||
|         friend class WebSocketServer; |         friend class WebSocketServer; | ||||||
|     }; |     }; | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -87,6 +87,7 @@ namespace ix | |||||||
|     WebSocketInitResult WebSocketHandshake::clientHandshake( |     WebSocketInitResult WebSocketHandshake::clientHandshake( | ||||||
|         const std::string& url, |         const std::string& url, | ||||||
|         const WebSocketHttpHeaders& extraHeaders, |         const WebSocketHttpHeaders& extraHeaders, | ||||||
|  |         const std::string& protocol, | ||||||
|         const std::string& host, |         const std::string& host, | ||||||
|         const std::string& path, |         const std::string& path, | ||||||
|         int port, |         int port, | ||||||
| @@ -125,6 +126,12 @@ namespace ix | |||||||
|             ss << "User-Agent: " << userAgent() << "\r\n"; |             ss << "User-Agent: " << userAgent() << "\r\n"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // Set an origin header if missing | ||||||
|  |         if (extraHeaders.find("Origin") == extraHeaders.end()) | ||||||
|  |         { | ||||||
|  |             ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         for (auto& it : extraHeaders) |         for (auto& it : extraHeaders) | ||||||
|         { |         { | ||||||
|             ss << it.first << ": " << it.second << "\r\n"; |             ss << it.first << ": " << it.second << "\r\n"; | ||||||
| @@ -240,28 +247,42 @@ namespace ix | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs, |     WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs, | ||||||
|                                                             bool enablePerMessageDeflate) |                                                             bool enablePerMessageDeflate, | ||||||
|  |                                                             HttpRequestPtr request) | ||||||
|     { |     { | ||||||
|         _requestInitCancellation = false; |         _requestInitCancellation = false; | ||||||
|  |  | ||||||
|         auto isCancellationRequested = |         auto isCancellationRequested = | ||||||
|             makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation); |             makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation); | ||||||
|  |  | ||||||
|         // Read first line |         std::string method; | ||||||
|         auto lineResult = _socket->readLine(isCancellationRequested); |         std::string uri; | ||||||
|         auto lineValid = lineResult.first; |         std::string httpVersion; | ||||||
|         auto line = lineResult.second; |  | ||||||
|  |  | ||||||
|         if (!lineValid) |         if (request) | ||||||
|         { |         { | ||||||
|             return sendErrorResponse(400, "Error reading HTTP request line"); |             method = request->method; | ||||||
|  |             uri = request->uri; | ||||||
|  |             httpVersion = request->version; | ||||||
|         } |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             // Read first line | ||||||
|  |             auto lineResult = _socket->readLine(isCancellationRequested); | ||||||
|  |             auto lineValid = lineResult.first; | ||||||
|  |             auto line = lineResult.second; | ||||||
|  |  | ||||||
|         // Validate request line (GET /foo HTTP/1.1\r\n) |             if (!lineValid) | ||||||
|         auto requestLine = Http::parseRequestLine(line); |             { | ||||||
|         auto method = std::get<0>(requestLine); |                 return sendErrorResponse(400, "Error reading HTTP request line"); | ||||||
|         auto uri = std::get<1>(requestLine); |             } | ||||||
|         auto httpVersion = std::get<2>(requestLine); |  | ||||||
|  |             // Validate request line (GET /foo HTTP/1.1\r\n) | ||||||
|  |             auto requestLine = Http::parseRequestLine(line); | ||||||
|  |             method = std::get<0>(requestLine); | ||||||
|  |             uri = std::get<1>(requestLine); | ||||||
|  |             httpVersion = std::get<2>(requestLine); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (method != "GET") |         if (method != "GET") | ||||||
|         { |         { | ||||||
| @@ -274,14 +295,22 @@ namespace ix | |||||||
|                                      "Invalid HTTP version, need HTTP/1.1, got: " + httpVersion); |                                      "Invalid HTTP version, need HTTP/1.1, got: " + httpVersion); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Retrieve and validate HTTP headers |         WebSocketHttpHeaders headers; | ||||||
|         auto result = parseHttpHeaders(_socket, isCancellationRequested); |         if (request) | ||||||
|         auto headersValid = result.first; |  | ||||||
|         auto headers = result.second; |  | ||||||
|  |  | ||||||
|         if (!headersValid) |  | ||||||
|         { |         { | ||||||
|             return sendErrorResponse(400, "Error parsing HTTP headers"); |             headers = request->headers; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             // Retrieve and validate HTTP headers | ||||||
|  |             auto result = parseHttpHeaders(_socket, isCancellationRequested); | ||||||
|  |             auto headersValid = result.first; | ||||||
|  |             headers = result.second; | ||||||
|  |  | ||||||
|  |             if (!headersValid) | ||||||
|  |             { | ||||||
|  |                 return sendErrorResponse(400, "Error parsing HTTP headers"); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (headers.find("sec-websocket-key") == headers.end()) |         if (headers.find("sec-websocket-key") == headers.end()) | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXCancellationRequest.h" | #include "IXCancellationRequest.h" | ||||||
|  | #include "IXHttp.h" | ||||||
| #include "IXSocket.h" | #include "IXSocket.h" | ||||||
| #include "IXWebSocketHttpHeaders.h" | #include "IXWebSocketHttpHeaders.h" | ||||||
| #include "IXWebSocketInitResult.h" | #include "IXWebSocketInitResult.h" | ||||||
| @@ -30,12 +31,15 @@ namespace ix | |||||||
|  |  | ||||||
|         WebSocketInitResult clientHandshake(const std::string& url, |         WebSocketInitResult clientHandshake(const std::string& url, | ||||||
|                                             const WebSocketHttpHeaders& extraHeaders, |                                             const WebSocketHttpHeaders& extraHeaders, | ||||||
|  |                                             const std::string& protocol, | ||||||
|                                             const std::string& host, |                                             const std::string& host, | ||||||
|                                             const std::string& path, |                                             const std::string& path, | ||||||
|                                             int port, |                                             int port, | ||||||
|                                             int timeoutSecs); |                                             int timeoutSecs); | ||||||
|  |  | ||||||
|         WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate); |         WebSocketInitResult serverHandshake(int timeoutSecs, | ||||||
|  |                                             bool enablePerMessageDeflate, | ||||||
|  |                                             HttpRequestPtr request = nullptr); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         std::string genRandomString(const int len); |         std::string genRandomString(const int len); | ||||||
|   | |||||||
| @@ -46,6 +46,8 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
|  |  | ||||||
| #include "IXWebSocketPerMessageDeflate.h" | #include "IXWebSocketPerMessageDeflate.h" | ||||||
|  |  | ||||||
| #include "IXUniquePtr.h" | #include "IXUniquePtr.h" | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
| #include "zlib.h" | #include "zlib.h" | ||||||
| #endif | #endif | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <cstdint> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "IXWebSocketSendData.h" | #include "IXWebSocketSendData.h" | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ namespace ix | |||||||
|         server.setOnConnectionCallback( |         server.setOnConnectionCallback( | ||||||
|             [remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket, |             [remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket, | ||||||
|                                            std::shared_ptr<ConnectionState> connectionState) { |                                            std::shared_ptr<ConnectionState> connectionState) { | ||||||
|                 auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState); |                 auto state = std::static_pointer_cast<ProxyConnectionState>(connectionState); | ||||||
|                 auto remoteIp = connectionState->getRemoteIp(); |                 auto remoteIp = connectionState->getRemoteIp(); | ||||||
|  |  | ||||||
|                 // Server connection |                 // Server connection | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <iterator> | #include <iterator> | ||||||
|   | |||||||
| @@ -78,10 +78,22 @@ namespace ix | |||||||
|  |  | ||||||
|     void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket, |     void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket, | ||||||
|                                            std::shared_ptr<ConnectionState> connectionState) |                                            std::shared_ptr<ConnectionState> connectionState) | ||||||
|  |     { | ||||||
|  |         handleUpgrade(std::move(socket), connectionState); | ||||||
|  |  | ||||||
|  |         connectionState->setTerminated(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketServer::handleUpgrade(std::unique_ptr<Socket> socket, | ||||||
|  |                                         std::shared_ptr<ConnectionState> connectionState, | ||||||
|  |                                         HttpRequestPtr request) | ||||||
|     { |     { | ||||||
|         setThreadName("Srv:ws:" + connectionState->getId()); |         setThreadName("Srv:ws:" + connectionState->getId()); | ||||||
|  |  | ||||||
|         auto webSocket = std::make_shared<WebSocket>(); |         auto webSocket = std::make_shared<WebSocket>(); | ||||||
|  |  | ||||||
|  |         webSocket->setAutoThreadName(false); | ||||||
|  |  | ||||||
|         if (_onConnectionCallback) |         if (_onConnectionCallback) | ||||||
|         { |         { | ||||||
|             _onConnectionCallback(webSocket, connectionState); |             _onConnectionCallback(webSocket, connectionState); | ||||||
| @@ -89,7 +101,7 @@ namespace ix | |||||||
|             if (!webSocket->isOnMessageCallbackRegistered()) |             if (!webSocket->isOnMessageCallbackRegistered()) | ||||||
|             { |             { | ||||||
|                 logError("WebSocketServer Application developer error: Server callback improperly " |                 logError("WebSocketServer Application developer error: Server callback improperly " | ||||||
|                          "registerered."); |                          "registered."); | ||||||
|                 logError("Missing call to setOnMessageCallback inside setOnConnectionCallback."); |                 logError("Missing call to setOnMessageCallback inside setOnConnectionCallback."); | ||||||
|                 connectionState->setTerminated(); |                 connectionState->setTerminated(); | ||||||
|                 return; |                 return; | ||||||
| @@ -99,9 +111,8 @@ namespace ix | |||||||
|         { |         { | ||||||
|             WebSocket* webSocketRawPtr = webSocket.get(); |             WebSocket* webSocketRawPtr = webSocket.get(); | ||||||
|             webSocket->setOnMessageCallback( |             webSocket->setOnMessageCallback( | ||||||
|                 [this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) { |                 [this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) | ||||||
|                     _onClientMessageCallback(connectionState, *webSocketRawPtr, msg); |                 { _onClientMessageCallback(connectionState, *webSocketRawPtr, msg); }); | ||||||
|                 }); |  | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
| @@ -130,7 +141,7 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         auto status = webSocket->connectToSocket( |         auto status = webSocket->connectToSocket( | ||||||
|             std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate); |             std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate, request); | ||||||
|         if (status.success) |         if (status.success) | ||||||
|         { |         { | ||||||
|             // Process incoming messages and execute callbacks |             // Process incoming messages and execute callbacks | ||||||
| @@ -155,8 +166,6 @@ namespace ix | |||||||
|                 logError("Cannot delete client"); |                 logError("Cannot delete client"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         connectionState->setTerminated(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::set<std::shared_ptr<WebSocket>> WebSocketServer::getClients() |     std::set<std::shared_ptr<WebSocket>> WebSocketServer::getClients() | ||||||
| @@ -176,28 +185,30 @@ namespace ix | |||||||
|     // |     // | ||||||
|     void WebSocketServer::makeBroadcastServer() |     void WebSocketServer::makeBroadcastServer() | ||||||
|     { |     { | ||||||
|         setOnClientMessageCallback([this](std::shared_ptr<ConnectionState> connectionState, |         setOnClientMessageCallback( | ||||||
|                                           WebSocket& webSocket, |             [this](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                                           const WebSocketMessagePtr& msg) { |                    WebSocket& webSocket, | ||||||
|             auto remoteIp = connectionState->getRemoteIp(); |                    const WebSocketMessagePtr& msg) | ||||||
|             if (msg->type == ix::WebSocketMessageType::Message) |  | ||||||
|             { |             { | ||||||
|                 for (auto&& client : getClients()) |                 auto remoteIp = connectionState->getRemoteIp(); | ||||||
|  |                 if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|                 { |                 { | ||||||
|                     if (client.get() != &webSocket) |                     for (auto&& client : getClients()) | ||||||
|                     { |                     { | ||||||
|                         client->send(msg->str, msg->binary); |                         if (client.get() != &webSocket) | ||||||
|  |  | ||||||
|                         // Make sure the OS send buffer is flushed before moving on |  | ||||||
|                         do |  | ||||||
|                         { |                         { | ||||||
|                             std::chrono::duration<double, std::milli> duration(500); |                             client->send(msg->str, msg->binary); | ||||||
|                             std::this_thread::sleep_for(duration); |  | ||||||
|                         } while (client->bufferedAmount() != 0); |                             // Make sure the OS send buffer is flushed before moving on | ||||||
|  |                             do | ||||||
|  |                             { | ||||||
|  |                                 std::chrono::duration<double, std::milli> duration(500); | ||||||
|  |                                 std::this_thread::sleep_for(duration); | ||||||
|  |                             } while (client->bufferedAmount() != 0); | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             }); | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocketServer::listenAndStart() |     bool WebSocketServer::listenAndStart() | ||||||
|   | |||||||
| @@ -55,6 +55,7 @@ namespace ix | |||||||
|         int getHandshakeTimeoutSecs(); |         int getHandshakeTimeoutSecs(); | ||||||
|         bool isPongEnabled(); |         bool isPongEnabled(); | ||||||
|         bool isPerMessageDeflateEnabled(); |         bool isPerMessageDeflateEnabled(); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         // Member variables |         // Member variables | ||||||
|         int _handshakeTimeoutSecs; |         int _handshakeTimeoutSecs; | ||||||
| @@ -73,5 +74,10 @@ namespace ix | |||||||
|         virtual void handleConnection(std::unique_ptr<Socket> socket, |         virtual void handleConnection(std::unique_ptr<Socket> socket, | ||||||
|                                       std::shared_ptr<ConnectionState> connectionState); |                                       std::shared_ptr<ConnectionState> connectionState); | ||||||
|         virtual size_t getConnectedClientsCount() final; |         virtual size_t getConnectedClientsCount() final; | ||||||
|  |  | ||||||
|  |     protected: | ||||||
|  |         void handleUpgrade(std::unique_ptr<Socket> socket, | ||||||
|  |                            std::shared_ptr<ConnectionState> connectionState, | ||||||
|  |                            HttpRequestPtr request = nullptr); | ||||||
|     }; |     }; | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -45,7 +45,6 @@ | |||||||
| #include <cstdarg> | #include <cstdarg> | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <string> | #include <string> | ||||||
| #include <thread> | #include <thread> | ||||||
| @@ -54,7 +53,6 @@ | |||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     const std::string WebSocketTransport::kPingMessage("ixwebsocket::heartbeat"); |  | ||||||
|     const int WebSocketTransport::kDefaultPingIntervalSecs(-1); |     const int WebSocketTransport::kDefaultPingIntervalSecs(-1); | ||||||
|     const bool WebSocketTransport::kDefaultEnablePong(true); |     const bool WebSocketTransport::kDefaultEnablePong(true); | ||||||
|     const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300); |     const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300); | ||||||
| @@ -74,6 +72,9 @@ namespace ix | |||||||
|         , _enablePong(kDefaultEnablePong) |         , _enablePong(kDefaultEnablePong) | ||||||
|         , _pingIntervalSecs(kDefaultPingIntervalSecs) |         , _pingIntervalSecs(kDefaultPingIntervalSecs) | ||||||
|         , _pongReceived(false) |         , _pongReceived(false) | ||||||
|  |         , _setCustomMessage(false) | ||||||
|  |         , _kPingMessage("ixwebsocket::heartbeat") | ||||||
|  |         , _pingType(SendMessageKind::Ping) | ||||||
|         , _pingCount(0) |         , _pingCount(0) | ||||||
|         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) |         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) | ||||||
|     { |     { | ||||||
| @@ -139,7 +140,7 @@ namespace ix | |||||||
|                                                   _enablePerMessageDeflate); |                                                   _enablePerMessageDeflate); | ||||||
|  |  | ||||||
|             result = webSocketHandshake.clientHandshake( |             result = webSocketHandshake.clientHandshake( | ||||||
|                 remoteUrl, headers, host, path, port, timeoutSecs); |                 remoteUrl, headers, protocol, host, path, port, timeoutSecs); | ||||||
|  |  | ||||||
|             if (result.http_status >= 300 && result.http_status < 400) |             if (result.http_status >= 300 && result.http_status < 400) | ||||||
|             { |             { | ||||||
| @@ -170,7 +171,8 @@ namespace ix | |||||||
|     // Server |     // Server | ||||||
|     WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket, |     WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket, | ||||||
|                                                             int timeoutSecs, |                                                             int timeoutSecs, | ||||||
|                                                             bool enablePerMessageDeflate) |                                                             bool enablePerMessageDeflate, | ||||||
|  |                                                             HttpRequestPtr request) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_socketMutex); |         std::lock_guard<std::mutex> lock(_socketMutex); | ||||||
|  |  | ||||||
| @@ -187,7 +189,8 @@ namespace ix | |||||||
|                                               _perMessageDeflateOptions, |                                               _perMessageDeflateOptions, | ||||||
|                                               _enablePerMessageDeflate); |                                               _enablePerMessageDeflate); | ||||||
|  |  | ||||||
|         auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate); |         auto result = | ||||||
|  |             webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate, request); | ||||||
|         if (result.success) |         if (result.success) | ||||||
|         { |         { | ||||||
|             setReadyState(ReadyState::OPEN); |             setReadyState(ReadyState::OPEN); | ||||||
| @@ -248,13 +251,51 @@ namespace ix | |||||||
|         return now - _lastSendPingTimePoint > std::chrono::seconds(_pingIntervalSecs); |         return now - _lastSendPingTimePoint > std::chrono::seconds(_pingIntervalSecs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketSendInfo WebSocketTransport::sendHeartBeat() |     void WebSocketTransport::setPingMessage(const std::string& message, SendMessageKind pingType) | ||||||
|  |     { | ||||||
|  |         _setCustomMessage = true; | ||||||
|  |         _kPingMessage = message; | ||||||
|  |         _pingType = pingType; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     WebSocketSendInfo WebSocketTransport::sendHeartBeat(SendMessageKind pingMessage) | ||||||
|     { |     { | ||||||
|         _pongReceived = false; |         _pongReceived = false; | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << kPingMessage << "::" << _pingIntervalSecs << "s" |  | ||||||
|            << "::" << _pingCount++; |         ss << _kPingMessage; | ||||||
|         return sendPing(ss.str()); |         if (!_setCustomMessage) | ||||||
|  |         { | ||||||
|  |             ss << "::" << _pingIntervalSecs << "s" | ||||||
|  |                << "::" << _pingCount++; | ||||||
|  |         } | ||||||
|  |         if (pingMessage == SendMessageKind::Ping) | ||||||
|  |         { | ||||||
|  |             return sendPing(ss.str()); | ||||||
|  |         } | ||||||
|  |         else if (pingMessage == SendMessageKind::Binary) | ||||||
|  |         { | ||||||
|  |             WebSocketSendInfo info = sendBinary(ss.str(), nullptr); | ||||||
|  |             if (info.success) | ||||||
|  |             { | ||||||
|  |                 std::lock_guard<std::mutex> lck(_lastSendPingTimePointMutex); | ||||||
|  |                 _lastSendPingTimePoint = std::chrono::steady_clock::now(); | ||||||
|  |             } | ||||||
|  |             return info; | ||||||
|  |         } | ||||||
|  |         else if (pingMessage == SendMessageKind::Text) | ||||||
|  |         { | ||||||
|  |             WebSocketSendInfo info = sendText(ss.str(), nullptr); | ||||||
|  |             if (info.success) | ||||||
|  |             { | ||||||
|  |                 std::lock_guard<std::mutex> lck(_lastSendPingTimePointMutex); | ||||||
|  |                 _lastSendPingTimePoint = std::chrono::steady_clock::now(); | ||||||
|  |             } | ||||||
|  |             return info; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // unknow type ping message | ||||||
|  |         return {}; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocketTransport::closingDelayExceeded() |     bool WebSocketTransport::closingDelayExceeded() | ||||||
| @@ -270,7 +311,9 @@ namespace ix | |||||||
|         { |         { | ||||||
|             if (pingIntervalExceeded()) |             if (pingIntervalExceeded()) | ||||||
|             { |             { | ||||||
|                 if (!_pongReceived) |                 // If it is not a 'ping' message of ping type, there is no need to judge whether | ||||||
|  |                 // pong will receive it | ||||||
|  |                 if (_pingType == SendMessageKind::Ping && !_pongReceived) | ||||||
|                 { |                 { | ||||||
|                     // ping response (PONG) exceeds the maximum delay, close the connection |                     // ping response (PONG) exceeds the maximum delay, close the connection | ||||||
|                     close(WebSocketCloseConstants::kInternalErrorCode, |                     close(WebSocketCloseConstants::kInternalErrorCode, | ||||||
| @@ -278,7 +321,7 @@ namespace ix | |||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     sendHeartBeat(); |                     sendHeartBeat(_pingType); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -657,6 +700,7 @@ namespace ix | |||||||
|                 if (_readyState != ReadyState::CLOSING) |                 if (_readyState != ReadyState::CLOSING) | ||||||
|                 { |                 { | ||||||
|                     // send back the CLOSE frame |                     // send back the CLOSE frame | ||||||
|  |                     setReadyState(ReadyState::CLOSING); | ||||||
|                     sendCloseFrame(code, reason); |                     sendCloseFrame(code, reason); | ||||||
|  |  | ||||||
|                     wakeUpFromPoll(SelectInterrupt::kCloseRequest); |                     wakeUpFromPoll(SelectInterrupt::kCloseRequest); | ||||||
| @@ -1029,7 +1073,10 @@ namespace ix | |||||||
|             else if (ret <= 0) |             else if (ret <= 0) | ||||||
|             { |             { | ||||||
|                 closeSocket(); |                 closeSocket(); | ||||||
|                 setReadyState(ReadyState::CLOSED); |                 if (_readyState != ReadyState::CLOSING) | ||||||
|  |                 { | ||||||
|  |                     setReadyState(ReadyState::CLOSED); | ||||||
|  |                 } | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
| @@ -1127,7 +1174,22 @@ namespace ix | |||||||
|     { |     { | ||||||
|         _requestInitCancellation = true; |         _requestInitCancellation = true; | ||||||
|  |  | ||||||
|         if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return; |         if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) | ||||||
|  |         { | ||||||
|  |             // Wake up the socket polling thread, as | ||||||
|  |             // Socket::isReadyToRead() might be still waiting the | ||||||
|  |             // interrupt event to happen. | ||||||
|  |             bool wakeUpPoll = false; | ||||||
|  |             { | ||||||
|  |               std::lock_guard<std::mutex> lock(_socketMutex); | ||||||
|  |               wakeUpPoll = (_socket && _socket->isWakeUpFromPollSupported()); | ||||||
|  |             } | ||||||
|  |             if (wakeUpPoll) | ||||||
|  |             { | ||||||
|  |                 wakeUpFromPoll(SelectInterrupt::kCloseRequest); | ||||||
|  |             } | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (closeWireSize == 0) |         if (closeWireSize == 0) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -18,9 +18,10 @@ | |||||||
| #include "IXWebSocketHttpHeaders.h" | #include "IXWebSocketHttpHeaders.h" | ||||||
| #include "IXWebSocketPerMessageDeflate.h" | #include "IXWebSocketPerMessageDeflate.h" | ||||||
| #include "IXWebSocketPerMessageDeflateOptions.h" | #include "IXWebSocketPerMessageDeflateOptions.h" | ||||||
| #include "IXWebSocketSendInfo.h" |  | ||||||
| #include "IXWebSocketSendData.h" | #include "IXWebSocketSendData.h" | ||||||
|  | #include "IXWebSocketSendInfo.h" | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | #include <cstdint> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <list> | #include <list> | ||||||
| #include <memory> | #include <memory> | ||||||
| @@ -86,7 +87,8 @@ namespace ix | |||||||
|         // Server |         // Server | ||||||
|         WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, |         WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, | ||||||
|                                             int timeoutSecs, |                                             int timeoutSecs, | ||||||
|                                             bool enablePerMessageDeflate); |                                             bool enablePerMessageDeflate, | ||||||
|  |                                             HttpRequestPtr request = nullptr); | ||||||
|  |  | ||||||
|         PollResult poll(); |         PollResult poll(); | ||||||
|         WebSocketSendInfo sendBinary(const IXWebSocketSendData& message, |         WebSocketSendInfo sendBinary(const IXWebSocketSendData& message, | ||||||
| @@ -108,8 +110,12 @@ namespace ix | |||||||
|         void dispatch(PollResult pollResult, const OnMessageCallback& onMessageCallback); |         void dispatch(PollResult pollResult, const OnMessageCallback& onMessageCallback); | ||||||
|         size_t bufferedAmount() const; |         size_t bufferedAmount() const; | ||||||
|  |  | ||||||
|  |         // set ping heartbeat message | ||||||
|  |         void setPingMessage(const std::string& message, SendMessageKind pingType); | ||||||
|  |  | ||||||
|         // internal |         // internal | ||||||
|         WebSocketSendInfo sendHeartBeat(); |         // send any type of ping packet, not only 'ping' type | ||||||
|  |         WebSocketSendInfo sendHeartBeat(SendMessageKind pingType); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         std::string _url; |         std::string _url; | ||||||
| @@ -214,7 +220,10 @@ namespace ix | |||||||
|         std::atomic<bool> _pongReceived; |         std::atomic<bool> _pongReceived; | ||||||
|  |  | ||||||
|         static const int kDefaultPingIntervalSecs; |         static const int kDefaultPingIntervalSecs; | ||||||
|         static const std::string kPingMessage; |  | ||||||
|  |         bool _setCustomMessage; | ||||||
|  |         std::string _kPingMessage; | ||||||
|  |         SendMessageKind _pingType; | ||||||
|         std::atomic<uint64_t> _pingCount; |         std::atomic<uint64_t> _pingCount; | ||||||
|  |  | ||||||
|         // We record when ping are being sent so that we can know when to send the next one |         // We record when ping are being sent so that we can know when to send the next one | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #define IX_WEBSOCKET_VERSION "11.4.3" | #define IX_WEBSOCKET_VERSION "11.4.4" | ||||||
|   | |||||||
| @@ -13,16 +13,24 @@ all: brew | |||||||
|  |  | ||||||
| install: brew | install: brew | ||||||
|  |  | ||||||
|  | -DCMAKE_INSTALL_PREFIX=/opt/homebrew | ||||||
|  |  | ||||||
| # Use -DCMAKE_INSTALL_PREFIX= to install into another location | # Use -DCMAKE_INSTALL_PREFIX= to install into another location | ||||||
| # on osx it is good practice to make /usr/local user writable | # on osx it is good practice to make /usr/local user writable | ||||||
| # sudo chown -R `whoami`/staff /usr/local | # sudo chown -R `whoami`/staff /usr/local | ||||||
| # | # | ||||||
|  | # Those days (since Apple Silicon mac shipped), on macOS homebrew installs in /opt/homebrew, and /usr/local is readonly | ||||||
|  | # | ||||||
| # Release, Debug, MinSizeRel, RelWithDebInfo are the build types | # Release, Debug, MinSizeRel, RelWithDebInfo are the build types | ||||||
| # | # | ||||||
| # Default rule does not use python as that requires first time users to have Python3 installed | # Default rule does not use python as that requires first time users to have Python3 installed | ||||||
| # | # | ||||||
| brew: | brew: | ||||||
|  | ifeq ($(shell uname),Darwin) | ||||||
|  | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/homebrew -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | ||||||
|  | else | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | ||||||
|  | endif | ||||||
|  |  | ||||||
| # Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the | # Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the | ||||||
| # server side ?) and I can't work-around it easily, so we're using mbedtls on | # server side ?) and I can't work-around it easily, so we're using mbedtls on | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								test/.certs/wrong-name-server-crt.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								test/.certs/wrong-name-server-crt.pem
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | -----BEGIN CERTIFICATE----- | ||||||
|  | MIIDNDCCAhwCFCl+O/rR8flqYKKvD0iwkucFwMaLMA0GCSqGSIb3DQEBCwUAMEEx | ||||||
|  | FDASBgNVBAoMC21hY2hpbmV6b25lMRQwEgYDVQQKDAtJWFdlYlNvY2tldDETMBEG | ||||||
|  | A1UEAwwKdHJ1c3RlZC1jYTAgFw0yMjA4MjMyMDM2MjVaGA80MjgxMDYwMTIwMzYy | ||||||
|  | NVowajELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMREwDwYDVQQHDAhCZXJrZWxl | ||||||
|  | eTEbMBkGA1UECgwSRHVtbXkgT3JnYW5pemF0aW9uMR4wHAYDVQQDDBVub3QuYS52 | ||||||
|  | YWxpZC5ob3N0Lm5hbWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2 | ||||||
|  | 9N806IjCvA82zfk9CPNwaEHOygNDJSXaZ38YDSI4C+Wf2imnMxrLQKaoccHUi+9L | ||||||
|  | 4rQN/hSCg+uTULQUZ0iyssGDaIh4IcAeoEcNlXYHTrgP+aAaby3q5zeZ80K3+6e4 | ||||||
|  | rqcuBPV2lLszJu3XXwEKbDSxW3De0gc2N8m9DN8Lx7i70DRf0F4m6RIMFF/kHXwa | ||||||
|  | zZLeG7rZb4xL684lmmQsWtk5Z600CvrBtUE7fQ7bhy0QhSt66kdTSL8IKQrbWcGj | ||||||
|  | q0tggFlOqeyZSi73gqUiAIuGO8/tRgmp4HygNyC24jpOB5nObOPPJTUEf5Mk1Bum | ||||||
|  | kD5a+yL6YbVdhiaK17wbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKsLXGLfO1IZ | ||||||
|  | LbofGc7/TCQwRayR3RdG4864PBy97KfJWyizg7Wm4X4yPFRG+6q3X5NKW32Ew9lI | ||||||
|  | jXulXCTjWOiSxiG4Pk20uczkOhWVHFdnS9gZvykPC/ElxBKPalT6MMstZWxpElsk | ||||||
|  | rCDKXj4LkD0po8bZrjlgSZQQQk6XMRkoRai2GWLJqIjaNCSF8nqb1wM/1OE1tAwi | ||||||
|  | polO1eFMA24yypvlXcNrNXjqcQ7LaoQFQltmi/RV+uTk7EK2F2jgYVzJ/pe2ET0i | ||||||
|  | RIMbGZTlAiemDGL9SpMsxftG6fSmP6QqDqYExmmPlZMLprb2da/2kelWFA+VkvdG | ||||||
|  | zFrnIcyfMY4= | ||||||
|  | -----END CERTIFICATE----- | ||||||
							
								
								
									
										27
									
								
								test/.certs/wrong-name-server-key.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								test/.certs/wrong-name-server-key.pem
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | -----BEGIN RSA PRIVATE KEY----- | ||||||
|  | MIIEpAIBAAKCAQEAtvTfNOiIwrwPNs35PQjzcGhBzsoDQyUl2md/GA0iOAvln9op | ||||||
|  | pzMay0CmqHHB1IvvS+K0Df4UgoPrk1C0FGdIsrLBg2iIeCHAHqBHDZV2B064D/mg | ||||||
|  | Gm8t6uc3mfNCt/unuK6nLgT1dpS7Mybt118BCmw0sVtw3tIHNjfJvQzfC8e4u9A0 | ||||||
|  | X9BeJukSDBRf5B18Gs2S3hu62W+MS+vOJZpkLFrZOWetNAr6wbVBO30O24ctEIUr | ||||||
|  | eupHU0i/CCkK21nBo6tLYIBZTqnsmUou94KlIgCLhjvP7UYJqeB8oDcgtuI6TgeZ | ||||||
|  | zmzjzyU1BH+TJNQbppA+Wvsi+mG1XYYmite8GwIDAQABAoIBAGRzAXG9EglI01mV | ||||||
|  | sPfvyCi5NRhiFXRyGtxU4pTD8TuwXHxtfV0NU/KwJlBpVLBrvBCAAbeE/qHB6D9T | ||||||
|  | metx4ZorRs/tPrAmZ6LpANnWa50LfUdYGK0qyZ0lIYPm6YS2KJnfWm6LznEyq60j | ||||||
|  | /IW45YthaXTO7aOI0OjVrG+dd4CxU1g1NtCQ9bdDMDjfXFVnoOifXIl8W22eRMoZ | ||||||
|  | LzCz+0sI0R0LenXCPT566de21F0NDkIK7NaQ1l5BMX4PA+RsN3cZlzyruA1woPKI | ||||||
|  | aBR2LQGNrBfDVGMATtUm89RpWAftb8FmXqYUsM7zAzTO6dViitiB7OFlw7Ax15YH | ||||||
|  | MTj5zGECgYEA35ocEEMfyahBN70bjyiqOHlzKwFjDl9DsUf8xqHsNhYAL+GrOK9A | ||||||
|  | 8lN5ByzcnbV3TJtU4WYbPgQJld8gXFx4h3eS+SkA/ASkAdtgHfdMImZ1v7k3TIPf | ||||||
|  | DXOCsHzELsQY6OgiI572Nwzx/Tl+0dFwaOfLjU9iEmmqL667j1Y4NiMCgYEA0Xch | ||||||
|  | 9K/vwZ1I9gM3ySvG40R2TRriC9Bf8jwrEWeRsWNvBcqtMMrgwAMsMCKDugSZR7n6 | ||||||
|  | o3WZV6mpvYVLFx0b93v07i7EpFP27kMw3gLNBKX62snR9JbqwAMK7tktgLds5pKM | ||||||
|  | DvLHuAQ9XMMXMLX7WlSyhmtFeU7IDulTSHHqdakCgYEAywITCpy2xpKRK7bwx4gH | ||||||
|  | C6EQc/IdahYJ0nHmSL0IRY6x+sbrelp7H8ezcVVEs5bmylGYvc/DWgm2XjCnI9P8 | ||||||
|  | xhlFAhw9PZJFCT6QRISaxfy6WSgi0cBEieTeubd9MmxtpT/khuyy5AZHyj0iLAL4 | ||||||
|  | CPayMwjopIj0r7f3p8qC3HsCgYBmq2kmYVI4aZrIkv02CtIatYTy+DlSJxnQRvOp | ||||||
|  | PUWpWB6kDRrk7pxJIYT4NwKwG+7xvFQA6PR3hn7fmUUcGDWMEeMVGDFkho9ja+W4 | ||||||
|  | /FB3dc/Gi+PwakS4RwWF20e1brLfNXeXICMKrHFTVYC5bIm+VgOHZW8RLa9bt7wN | ||||||
|  | p2CPuQKBgQCjW+rCODmMzEues/I143mYMDdZ1IschtWGiXBNrpkHm/DcZSutbacm | ||||||
|  | 5C7Kwv44pfA90NHDTjuaIgRgfeUTawkrl8uPXEuj80mW72agf5oS8lJzD+2jibCj | ||||||
|  | Q4K52G+0LaTxHLZxufil28Rgja01c0mTcuLbhKtCgHl5EHP19wOKEg== | ||||||
|  | -----END RSA PRIVATE KEY----- | ||||||
| @@ -16,7 +16,7 @@ set (TEST_TARGET_NAMES | |||||||
|   IXWebSocketServerTest |   IXWebSocketServerTest | ||||||
|   IXWebSocketTestConnectionDisconnection |   IXWebSocketTestConnectionDisconnection | ||||||
|   IXUrlParserTest |   IXUrlParserTest | ||||||
|   IXHttpClientTest |   # IXHttpClientTest ## FIXME httpbin.org does not seem normal | ||||||
|   IXUnityBuildsTest |   IXUnityBuildsTest | ||||||
|   IXHttpTest |   IXHttpTest | ||||||
|   IXDNSLookupTest |   IXDNSLookupTest | ||||||
| @@ -24,14 +24,13 @@ set (TEST_TARGET_NAMES | |||||||
|   # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws |   # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws | ||||||
|   IXStrCaseCompareTest |   IXStrCaseCompareTest | ||||||
|   IXExponentialBackoffTest |   IXExponentialBackoffTest | ||||||
|  |   IXWebSocketCloseTest | ||||||
| ) | ) | ||||||
|  |  | ||||||
| # Some unittest don't work on windows yet | # Some unittest don't work on windows yet | ||||||
| # Windows without TLS does not have hmac yet | # Windows without TLS does not have hmac yet | ||||||
| if (UNIX) | if (UNIX) | ||||||
|   list(APPEND TEST_TARGET_NAMES |   list(APPEND TEST_TARGET_NAMES | ||||||
|     IXWebSocketCloseTest |  | ||||||
|  |  | ||||||
|     # Fail on Windows in CI probably because the pathing is wrong and |     # Fail on Windows in CI probably because the pathing is wrong and | ||||||
|     # some resource files cannot be found |     # some resource files cannot be found | ||||||
|     IXHttpServerTest |     IXHttpServerTest | ||||||
|   | |||||||
| @@ -19,13 +19,9 @@ TEST_CASE("dns", "[net]") | |||||||
|         auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80); |         auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80); | ||||||
|  |  | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         struct addrinfo* res; |         auto res = dnsLookup->resolve(errMsg, [] { return false; }); | ||||||
|  |  | ||||||
|         res = dnsLookup->resolve(errMsg, [] { return false; }); |  | ||||||
|         std::cerr << "Error message: " << errMsg << std::endl; |         std::cerr << "Error message: " << errMsg << std::endl; | ||||||
|         REQUIRE(res != nullptr); |         REQUIRE(res != nullptr); | ||||||
|  |  | ||||||
|         dnsLookup->release(res); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Test resolving a non-existing hostname") |     SECTION("Test resolving a non-existing hostname") | ||||||
| @@ -33,7 +29,7 @@ TEST_CASE("dns", "[net]") | |||||||
|         auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); |         auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); | ||||||
|  |  | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; }); |         auto res = dnsLookup->resolve(errMsg, [] { return false; }); | ||||||
|         std::cerr << "Error message: " << errMsg << std::endl; |         std::cerr << "Error message: " << errMsg << std::endl; | ||||||
|         REQUIRE(res == nullptr); |         REQUIRE(res == nullptr); | ||||||
|     } |     } | ||||||
| @@ -44,7 +40,7 @@ TEST_CASE("dns", "[net]") | |||||||
|  |  | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         // The callback returning true means we are requesting cancellation |         // The callback returning true means we are requesting cancellation | ||||||
|         struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; }); |         auto res = dnsLookup->resolve(errMsg, [] { return true; }); | ||||||
|         std::cerr << "Error message: " << errMsg << std::endl; |         std::cerr << "Error message: " << errMsg << std::endl; | ||||||
|         REQUIRE(res == nullptr); |         REQUIRE(res == nullptr); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -7,7 +7,9 @@ | |||||||
| #include "catch.hpp" | #include "catch.hpp" | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  | #include <ixwebsocket/IXGetFreePort.h> | ||||||
| #include <ixwebsocket/IXHttpClient.h> | #include <ixwebsocket/IXHttpClient.h> | ||||||
|  | #include <ixwebsocket/IXHttpServer.h> | ||||||
|  |  | ||||||
| using namespace ix; | using namespace ix; | ||||||
|  |  | ||||||
| @@ -95,6 +97,52 @@ TEST_CASE("http_client", "[http]") | |||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #if defined(IXWEBSOCKET_USE_TLS) && !defined(IXWEBSOCKET_USE_SECURE_TRANSPORT) | ||||||
|  |     SECTION("Disable hostname validation") | ||||||
|  |     { | ||||||
|  |         static auto test_cert_with_wrong_name = [](bool validate_hostname) | ||||||
|  |         { | ||||||
|  |             int port = getFreePort(); | ||||||
|  |             ix::HttpServer server(port, "127.0.0.1"); | ||||||
|  |  | ||||||
|  |             SocketTLSOptions tlsOptionsServer; | ||||||
|  |             tlsOptionsServer.tls = true; | ||||||
|  |             tlsOptionsServer.caFile = "NONE"; | ||||||
|  |             tlsOptionsServer.certFile = "./.certs/wrong-name-server-crt.pem"; | ||||||
|  |             tlsOptionsServer.keyFile = "./.certs/wrong-name-server-key.pem"; | ||||||
|  |             server.setTLSOptions(tlsOptionsServer); | ||||||
|  |  | ||||||
|  |             auto res = server.listen(); | ||||||
|  |             REQUIRE(res.first); | ||||||
|  |             server.start(); | ||||||
|  |  | ||||||
|  |             HttpClient httpClient; | ||||||
|  |             SocketTLSOptions tlsOptionsClient; | ||||||
|  |             tlsOptionsClient.caFile = "./.certs/trusted-ca-crt.pem"; | ||||||
|  |             tlsOptionsClient.disable_hostname_validation = validate_hostname; | ||||||
|  |             httpClient.setTLSOptions(tlsOptionsClient); | ||||||
|  |  | ||||||
|  |             std::string url("https://localhost:" + std::to_string(port)); | ||||||
|  |             auto args = httpClient.createRequest(url); | ||||||
|  |             args->connectTimeout = 10; | ||||||
|  |             args->transferTimeout = 10; | ||||||
|  |  | ||||||
|  |             auto response = httpClient.get(url, args); | ||||||
|  |  | ||||||
|  |             std::cerr << "Status: " << response->statusCode << std::endl; | ||||||
|  |             std::cerr << "Error code: " << (int) response->errorCode << std::endl; | ||||||
|  |             std::cerr << "Error message: " << response->errorMsg << std::endl; | ||||||
|  |  | ||||||
|  |             server.stop(); | ||||||
|  |             return std::make_tuple(response->errorCode, response->statusCode); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         REQUIRE(test_cert_with_wrong_name(false) == | ||||||
|  |                 std::make_tuple(HttpErrorCode::CannotConnect, 0)); | ||||||
|  |         REQUIRE(test_cert_with_wrong_name(true) == std::make_tuple(HttpErrorCode::Ok, 404)); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     SECTION("Async API, one call") |     SECTION("Async API, one call") | ||||||
|     { |     { | ||||||
|         bool async = true; |         bool async = true; | ||||||
|   | |||||||
| @@ -60,6 +60,7 @@ TEST_CASE("http server", "[httpd]") | |||||||
|         REQUIRE(response->errorCode == HttpErrorCode::Ok); |         REQUIRE(response->errorCode == HttpErrorCode::Ok); | ||||||
|         REQUIRE(response->statusCode == 200); |         REQUIRE(response->statusCode == 200); | ||||||
|         REQUIRE(response->headers["Accept-Encoding"] == "gzip"); |         REQUIRE(response->headers["Accept-Encoding"] == "gzip"); | ||||||
|  |         REQUIRE(response->headers["Content-Encoding"] == "gzip"); | ||||||
|  |  | ||||||
|         server.stop(); |         server.stop(); | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								ws/ws.cpp
									
									
									
									
									
								
							| @@ -77,24 +77,6 @@ namespace | |||||||
|         return std::make_pair(res.first, std::string(vec.begin(), vec.end())); |         return std::make_pair(res.first, std::string(vec.begin(), vec.end())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Assume the file exists |  | ||||||
|     std::string readBytes(const std::string& path) |  | ||||||
|     { |  | ||||||
|         std::vector<uint8_t> memblock; |  | ||||||
|         std::ifstream file(path); |  | ||||||
|  |  | ||||||
|         file.seekg(0, file.end); |  | ||||||
|         std::streamoff size = file.tellg(); |  | ||||||
|         file.seekg(0, file.beg); |  | ||||||
|  |  | ||||||
|         memblock.reserve((size_t) size); |  | ||||||
|         memblock.insert( |  | ||||||
|             memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>()); |  | ||||||
|  |  | ||||||
|         std::string bytes(memblock.begin(), memblock.end()); |  | ||||||
|         return bytes; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     std::string truncate(const std::string& str, size_t n) |     std::string truncate(const std::string& str, size_t n) | ||||||
|     { |     { | ||||||
|         if (str.size() < n) |         if (str.size() < n) | ||||||
| @@ -107,12 +89,6 @@ namespace | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool fileExists(const std::string& fileName) |  | ||||||
|     { |  | ||||||
|         std::ifstream infile(fileName); |  | ||||||
|         return infile.good(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     std::string extractFilename(const std::string& path) |     std::string extractFilename(const std::string& path) | ||||||
|     { |     { | ||||||
|         std::string::size_type idx; |         std::string::size_type idx; | ||||||
| @@ -916,9 +892,8 @@ namespace ix | |||||||
|         auto dnsLookup = std::make_shared<DNSLookup>(hostname, 80); |         auto dnsLookup = std::make_shared<DNSLookup>(hostname, 80); | ||||||
|  |  | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         struct addrinfo* res; |  | ||||||
|  |  | ||||||
|         res = dnsLookup->resolve(errMsg, [] { return false; }); |         auto res = dnsLookup->resolve(errMsg, [] { return false; }); | ||||||
|  |  | ||||||
|         auto addr = res->ai_addr; |         auto addr = res->ai_addr; | ||||||
|  |  | ||||||
| @@ -2486,10 +2461,8 @@ int main(int argc, char** argv) | |||||||
|     bool verbose = false; |     bool verbose = false; | ||||||
|     bool save = false; |     bool save = false; | ||||||
|     bool quiet = false; |     bool quiet = false; | ||||||
|     bool fluentd = false; |  | ||||||
|     bool compress = false; |     bool compress = false; | ||||||
|     bool compressRequest = false; |     bool compressRequest = false; | ||||||
|     bool stress = false; |  | ||||||
|     bool disableAutomaticReconnection = false; |     bool disableAutomaticReconnection = false; | ||||||
|     bool disablePerMessageDeflate = false; |     bool disablePerMessageDeflate = false; | ||||||
|     bool greetings = false; |     bool greetings = false; | ||||||
| @@ -2505,7 +2478,6 @@ int main(int argc, char** argv) | |||||||
|     int transferTimeout = 1800; |     int transferTimeout = 1800; | ||||||
|     int maxRedirects = 5; |     int maxRedirects = 5; | ||||||
|     int delayMs = -1; |     int delayMs = -1; | ||||||
|     int count = 1; |  | ||||||
|     int msgCount = 1000 * 1000; |     int msgCount = 1000 * 1000; | ||||||
|     uint32_t maxWaitBetweenReconnectionRetries = 10 * 1000; // 10 seconds |     uint32_t maxWaitBetweenReconnectionRetries = 10 * 1000; // 10 seconds | ||||||
|     int pingIntervalSecs = 30; |     int pingIntervalSecs = 30; | ||||||
| @@ -2529,6 +2501,7 @@ int main(int argc, char** argv) | |||||||
|                         "A (comma/space/colon) separated list of ciphers to use for TLS"); |                         "A (comma/space/colon) separated list of ciphers to use for TLS"); | ||||||
|         app->add_flag("--tls", tlsOptions.tls, "Enable TLS (server only)"); |         app->add_flag("--tls", tlsOptions.tls, "Enable TLS (server only)"); | ||||||
|         app->add_flag("--verify_none", verifyNone, "Disable peer cert verification"); |         app->add_flag("--verify_none", verifyNone, "Disable peer cert verification"); | ||||||
|  |         app->add_flag("--disable-hostname-validation", tlsOptions.disable_hostname_validation, "Disable validation of certificates' hostnames"); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     app.add_flag("--version", version, "Print ws version"); |     app.add_flag("--version", version, "Print ws version"); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user