Compare commits
	
		
			23 Commits
		
	
	
		
			bsergean-p
			...
			feature/te
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 4a2a8e7b36 | ||
|  | f073203ac1 | ||
|  | 1866d94550 | ||
|  | 9157873f5b | ||
|  | aa2ca19895 | ||
|  | 6cc21f3658 | ||
|  | 679ce519dd | ||
|  | a5d4911a16 | ||
|  | b0fd119d14 | ||
|  | 472cf68c31 | ||
|  | 1e46466114 | ||
|  | 0b8b5608dc | ||
|  | 20a028e2ae | ||
|  | 8d7b557be6 | ||
|  | e417e63605 | ||
|  | 7b1524d7ec | ||
|  | e8048ad826 | ||
|  | 2b40a30c8f | ||
|  | d7bfe89e43 | ||
|  | 84aa652846 | ||
|  | edb6ded99f | ||
|  | 2f560ff4c0 | ||
|  | 002d9c8985 | 
							
								
								
									
										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' |  | ||||||
| @@ -11,6 +11,7 @@ project(ixwebsocket C CXX) | |||||||
| 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) | ||||||
|  |  | ||||||
| @@ -66,6 +67,7 @@ set( IXWEBSOCKET_SOURCES | |||||||
| ) | ) | ||||||
|  |  | ||||||
| set( IXWEBSOCKET_HEADERS | set( IXWEBSOCKET_HEADERS | ||||||
|  |     ixwebsocket/IXBase64.h | ||||||
|     ixwebsocket/IXBench.h |     ixwebsocket/IXBench.h | ||||||
|     ixwebsocket/IXCancellationRequest.h |     ixwebsocket/IXCancellationRequest.h | ||||||
|     ixwebsocket/IXConnectionState.h |     ixwebsocket/IXConnectionState.h | ||||||
| @@ -249,7 +251,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) | ||||||
|   | |||||||
| @@ -35,6 +35,7 @@ int main() | |||||||
|  |  | ||||||
|     // Connect to a server with encryption |     // Connect to a server with encryption | ||||||
|     // See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration |     // See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration | ||||||
|  |     //     https://github.com/machinezone/IXWebSocket/issues/386#issuecomment-1105235227 (self signed certificates) | ||||||
|     std::string url("wss://echo.websocket.org"); |     std::string url("wss://echo.websocket.org"); | ||||||
|     webSocket.setUrl(url); |     webSocket.setUrl(url); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,12 @@ | |||||||
|  |  | ||||||
| All changes to this project will be documented in this file. | All changes to this project will be documented in this file. | ||||||
|  |  | ||||||
|  | ## [11.4.3] - 2022-05-13 | ||||||
|  |  | ||||||
|  | Set shorter thread names | ||||||
|  | BoringSSL fix with SNI | ||||||
|  | Websocket computed header is valid Base64 | ||||||
|  |  | ||||||
| ## [11.4.1] - 2022-04-23 | ## [11.4.1] - 2022-04-23 | ||||||
|  |  | ||||||
| vckpg + cmake fix, to handle zlib as a dependency better | vckpg + cmake fix, to handle zlib as a dependency better | ||||||
|   | |||||||
| @@ -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`. | ||||||
|   | |||||||
							
								
								
									
										125
									
								
								ixwebsocket/IXBase64.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								ixwebsocket/IXBase64.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | #ifndef _MACARON_BASE64_H_ | ||||||
|  | #define _MACARON_BASE64_H_ | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * The MIT License (MIT) | ||||||
|  |  * Copyright (c) 2016 tomykaira | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining | ||||||
|  |  * a copy of this software and associated documentation files (the | ||||||
|  |  * "Software"), to deal in the Software without restriction, including | ||||||
|  |  * without limitation the rights to use, copy, modify, merge, publish, | ||||||
|  |  * distribute, sublicense, and/or sell copies of the Software, and to | ||||||
|  |  * permit persons to whom the Software is furnished to do so, subject to | ||||||
|  |  * the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be | ||||||
|  |  * included in all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||||
|  |  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||||
|  |  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||||||
|  |  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||||||
|  |  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||||||
|  |  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | namespace macaron { | ||||||
|  |  | ||||||
|  | class Base64 { | ||||||
|  |  public: | ||||||
|  |  | ||||||
|  |   static std::string Encode(const std::string data) { | ||||||
|  |     static constexpr char sEncodingTable[] = { | ||||||
|  |       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', | ||||||
|  |       'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', | ||||||
|  |       'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', | ||||||
|  |       'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', | ||||||
|  |       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', | ||||||
|  |       'o', 'p', 'q', 'r', 's', 't', 'u', 'v', | ||||||
|  |       'w', 'x', 'y', 'z', '0', '1', '2', '3', | ||||||
|  |       '4', '5', '6', '7', '8', '9', '+', '/' | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     size_t in_len = data.size(); | ||||||
|  |     size_t out_len = 4 * ((in_len + 2) / 3); | ||||||
|  |     std::string ret(out_len, '\0'); | ||||||
|  |     size_t i; | ||||||
|  |     char *p = const_cast<char*>(ret.c_str()); | ||||||
|  |  | ||||||
|  |     for (i = 0; i < in_len - 2; i += 3) { | ||||||
|  |       *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; | ||||||
|  |       *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; | ||||||
|  |       *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; | ||||||
|  |       *p++ = sEncodingTable[data[i + 2] & 0x3F]; | ||||||
|  |     } | ||||||
|  |     if (i < in_len) { | ||||||
|  |       *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; | ||||||
|  |       if (i == (in_len - 1)) { | ||||||
|  |         *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; | ||||||
|  |         *p++ = '='; | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; | ||||||
|  |         *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; | ||||||
|  |       } | ||||||
|  |       *p++ = '='; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static std::string Decode(const std::string& input, std::string& out) { | ||||||
|  |     static constexpr unsigned char kDecodingTable[] = { | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, | ||||||
|  |       52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, | ||||||
|  |       15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, | ||||||
|  |       64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | ||||||
|  |       41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | ||||||
|  |       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     size_t in_len = input.size(); | ||||||
|  |     if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; | ||||||
|  |  | ||||||
|  |     size_t out_len = in_len / 4 * 3; | ||||||
|  |     if (input[in_len - 1] == '=') out_len--; | ||||||
|  |     if (input[in_len - 2] == '=') out_len--; | ||||||
|  |  | ||||||
|  |     out.resize(out_len); | ||||||
|  |  | ||||||
|  |     for (size_t i = 0, j = 0; i < in_len;) { | ||||||
|  |       uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])]; | ||||||
|  |       uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])]; | ||||||
|  |       uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])]; | ||||||
|  |       uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])]; | ||||||
|  |  | ||||||
|  |       uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); | ||||||
|  |  | ||||||
|  |       if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; | ||||||
|  |       if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; | ||||||
|  |       if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ""; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif /* _MACARON_BASE64_H_ */ | ||||||
| @@ -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,6 +23,7 @@ | |||||||
| #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__) | ||||||
| @@ -44,7 +45,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 +64,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 +75,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 +90,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 +153,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 +177,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; | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
| @@ -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); | ||||||
|  |             std::shared_ptr<ix::HttpResponse> response; | ||||||
|  |             if (request->headers["Upgrade"] == "websocket") | ||||||
|  |             { | ||||||
|  |                 WebSocketServer::handleUpgrade(std::move(socket), connectionState, request); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 auto response = _onConnectionCallback(request, connectionState); | ||||||
|                 if (!Http::sendResponse(response, socket)) |                 if (!Http::sendResponse(response, socket)) | ||||||
|                 { |                 { | ||||||
|                     logError("Cannot send response"); |                     logError("Cannot send response"); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         connectionState->setTerminated(); |  | ||||||
|  |  | ||||||
|         _connectedClientsCount--; |  | ||||||
|         } |         } | ||||||
|  |         connectionState->setTerminated(); | ||||||
|     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); | ||||||
| @@ -165,9 +177,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 +210,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(); | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -6,6 +6,8 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
|  |  | ||||||
| #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,6 +205,8 @@ 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); | ||||||
|  |  | ||||||
|  |             if (!_tlsOptions.disable_hostname_validation) | ||||||
|                 SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); |                 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 | ||||||
| @@ -48,7 +49,7 @@ namespace ix | |||||||
|         mbedtls_pk_init(&_pkey); |         mbedtls_pk_init(&_pkey); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg) |     bool SocketMbedTLS::loadSystemCertificates(std::string& /* errorMsg */) | ||||||
|     { |     { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|         DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | |         DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | | ||||||
| @@ -195,11 +196,14 @@ namespace ix | |||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (!_tlsOptions.disable_hostname_validation) | ||||||
|  |         { | ||||||
|             if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0) |             if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0) | ||||||
|             { |             { | ||||||
|                 errMsg = "SNI setup failed"; |                 errMsg = "SNI setup failed"; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -301,7 +301,11 @@ namespace ix | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     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 +394,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 +763,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.) | ||||||
|  |             if (!_tlsOptions.disable_hostname_validation) | ||||||
|  |             { | ||||||
|                 X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection); |                 X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection); | ||||||
|             X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0); |                 X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size()); | ||||||
|  |             } | ||||||
| #endif | #endif | ||||||
|             handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested); |             handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -268,7 +268,10 @@ namespace ix | |||||||
|         // Set the socket to non blocking mode, so that accept calls are not blocking |         // Set the socket to non blocking mode, so that accept calls are not blocking | ||||||
|         SocketConnect::configure(_serverFd); |         SocketConnect::configure(_serverFd); | ||||||
|  |  | ||||||
|         setThreadName("SocketServer::accept"); |         // Use a cryptic name to stay within the 16 bytes limit thread name limitation | ||||||
|  |         // $ echo Srv:gc:64000 | wc -c | ||||||
|  |         // 13 | ||||||
|  |         setThreadName("Srv:ac:" + std::to_string(_port)); | ||||||
|  |  | ||||||
|         for (;;) |         for (;;) | ||||||
|         { |         { | ||||||
| @@ -425,7 +428,10 @@ namespace ix | |||||||
|  |  | ||||||
|     void SocketServer::runGC() |     void SocketServer::runGC() | ||||||
|     { |     { | ||||||
|         setThreadName("SocketServer::GC"); |         // Use a cryptic name to stay within the 16 bytes limit thread name limitation | ||||||
|  |         // $ echo Srv:gc:64000 | wc -c | ||||||
|  |         // 13 | ||||||
|  |         setThreadName("Srv:gc:" + std::to_string(_port)); | ||||||
|  |  | ||||||
|         for (;;) |         for (;;) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -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,11 @@ namespace ix | |||||||
|         , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) |         , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) | ||||||
|         , _enablePong(kDefaultEnablePong) |         , _enablePong(kDefaultEnablePong) | ||||||
|         , _pingIntervalSecs(kDefaultPingIntervalSecs) |         , _pingIntervalSecs(kDefaultPingIntervalSecs) | ||||||
|  |         , _pingType(SendMessageKind::Ping) | ||||||
|     { |     { | ||||||
|         _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 +103,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 +246,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 +254,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 +264,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 +281,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; | ||||||
| @@ -384,7 +399,8 @@ 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) | ||||||
|                     { |                     { | ||||||
| @@ -503,13 +519,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, | ||||||
|   | |||||||
| @@ -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; | ||||||
|  |  | ||||||
| @@ -128,7 +132,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,6 +174,8 @@ 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; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
|  |  | ||||||
| #include "IXWebSocketHandshake.h" | #include "IXWebSocketHandshake.h" | ||||||
|  |  | ||||||
|  | #include "IXBase64.h" | ||||||
| #include "IXHttp.h" | #include "IXHttp.h" | ||||||
| #include "IXSocketConnect.h" | #include "IXSocketConnect.h" | ||||||
| #include "IXStrCaseCompare.h" | #include "IXStrCaseCompare.h" | ||||||
| @@ -17,7 +18,6 @@ | |||||||
| #include <random> | #include <random> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     WebSocketHandshake::WebSocketHandshake( |     WebSocketHandshake::WebSocketHandshake( | ||||||
| @@ -106,15 +106,10 @@ namespace ix | |||||||
|             return WebSocketInitResult(false, 0, ss.str()); |             return WebSocketInitResult(false, 0, ss.str()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // |         // Generate a random 16 bytes string and base64 encode it. | ||||||
|         // Generate a random 24 bytes string which looks like it is base64 encoded |  | ||||||
|         // y3JJHMbDL1EzLkh9GBhXDw== |  | ||||||
|         // 0cb3Vd9HkbpVVumoS3Noka== |  | ||||||
|         // |         // | ||||||
|         // See https://stackoverflow.com/questions/18265128/what-is-sec-websocket-key-for |         // See https://stackoverflow.com/questions/18265128/what-is-sec-websocket-key-for | ||||||
|         // |         std::string secWebSocketKey = macaron::Base64::Encode(genRandomString(16)); | ||||||
|         std::string secWebSocketKey = genRandomString(22); |  | ||||||
|         secWebSocketKey += "=="; |  | ||||||
|  |  | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << "GET " << path << " HTTP/1.1\r\n"; |         ss << "GET " << path << " HTTP/1.1\r\n"; | ||||||
| @@ -245,13 +240,26 @@ 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); | ||||||
|  |  | ||||||
|  |         std::string method; | ||||||
|  |         std::string uri; | ||||||
|  |         std::string httpVersion; | ||||||
|  |  | ||||||
|  |         if (request) | ||||||
|  |         { | ||||||
|  |             method = request->method; | ||||||
|  |             uri = request->uri; | ||||||
|  |             httpVersion = request->version; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|             // Read first line |             // Read first line | ||||||
|             auto lineResult = _socket->readLine(isCancellationRequested); |             auto lineResult = _socket->readLine(isCancellationRequested); | ||||||
|             auto lineValid = lineResult.first; |             auto lineValid = lineResult.first; | ||||||
| @@ -264,9 +272,10 @@ namespace ix | |||||||
|  |  | ||||||
|             // Validate request line (GET /foo HTTP/1.1\r\n) |             // Validate request line (GET /foo HTTP/1.1\r\n) | ||||||
|             auto requestLine = Http::parseRequestLine(line); |             auto requestLine = Http::parseRequestLine(line); | ||||||
|         auto method = std::get<0>(requestLine); |             method = std::get<0>(requestLine); | ||||||
|         auto uri = std::get<1>(requestLine); |             uri = std::get<1>(requestLine); | ||||||
|         auto httpVersion = std::get<2>(requestLine); |             httpVersion = std::get<2>(requestLine); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (method != "GET") |         if (method != "GET") | ||||||
|         { |         { | ||||||
| @@ -279,15 +288,23 @@ namespace ix | |||||||
|                                      "Invalid HTTP version, need HTTP/1.1, got: " + httpVersion); |                                      "Invalid HTTP version, need HTTP/1.1, got: " + httpVersion); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         WebSocketHttpHeaders headers; | ||||||
|  |         if (request) | ||||||
|  |         { | ||||||
|  |             headers = request->headers; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|             // Retrieve and validate HTTP headers |             // Retrieve and validate HTTP headers | ||||||
|             auto result = parseHttpHeaders(_socket, isCancellationRequested); |             auto result = parseHttpHeaders(_socket, isCancellationRequested); | ||||||
|             auto headersValid = result.first; |             auto headersValid = result.first; | ||||||
|         auto headers = result.second; |             headers = result.second; | ||||||
|  |  | ||||||
|             if (!headersValid) |             if (!headersValid) | ||||||
|             { |             { | ||||||
|                 return sendErrorResponse(400, "Error parsing HTTP headers"); |                 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" | ||||||
| @@ -35,7 +36,9 @@ namespace ix | |||||||
|                                             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 | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <iterator> | #include <iterator> | ||||||
|   | |||||||
| @@ -79,7 +79,16 @@ 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) | ||||||
|     { |     { | ||||||
|         setThreadName("WebSocketServer::" + connectionState->getId()); |         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()); | ||||||
|  |  | ||||||
|         auto webSocket = std::make_shared<WebSocket>(); |         auto webSocket = std::make_shared<WebSocket>(); | ||||||
|         if (_onConnectionCallback) |         if (_onConnectionCallback) | ||||||
| @@ -89,7 +98,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 +108,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 +138,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 +163,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,9 +182,11 @@ namespace ix | |||||||
|     // |     // | ||||||
|     void WebSocketServer::makeBroadcastServer() |     void WebSocketServer::makeBroadcastServer() | ||||||
|     { |     { | ||||||
|         setOnClientMessageCallback([this](std::shared_ptr<ConnectionState> connectionState, |         setOnClientMessageCallback( | ||||||
|  |             [this](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                    WebSocket& webSocket, |                    WebSocket& webSocket, | ||||||
|                                           const WebSocketMessagePtr& msg) { |                    const WebSocketMessagePtr& msg) | ||||||
|  |             { | ||||||
|                 auto remoteIp = connectionState->getRemoteIp(); |                 auto remoteIp = connectionState->getRemoteIp(); | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Message) |                 if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|                 { |                 { | ||||||
|   | |||||||
| @@ -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); | ||||||
| @@ -73,6 +71,9 @@ namespace ix | |||||||
|         , _closingTimePoint(std::chrono::steady_clock::now()) |         , _closingTimePoint(std::chrono::steady_clock::now()) | ||||||
|         , _enablePong(kDefaultEnablePong) |         , _enablePong(kDefaultEnablePong) | ||||||
|         , _pingIntervalSecs(kDefaultPingIntervalSecs) |         , _pingIntervalSecs(kDefaultPingIntervalSecs) | ||||||
|  |         , _setCustomMessage(false) | ||||||
|  |         , _kPingMessage("ixwebsocket::heartbeat") | ||||||
|  |         , _pingType(SendMessageKind::Ping) | ||||||
|         , _pongReceived(false) |         , _pongReceived(false) | ||||||
|         , _pingCount(0) |         , _pingCount(0) | ||||||
|         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) |         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) | ||||||
| @@ -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,14 +251,52 @@ 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" |  | ||||||
|  |         ss << _kPingMessage; | ||||||
|  |         if (!_setCustomMessage) | ||||||
|  |         { | ||||||
|  |             ss << "::" << _pingIntervalSecs << "s" | ||||||
|                << "::" << _pingCount++; |                << "::" << _pingCount++; | ||||||
|  |         } | ||||||
|  |         if (pingMessage == SendMessageKind::Ping) | ||||||
|  |         { | ||||||
|             return sendPing(ss.str()); |             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); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -18,15 +18,17 @@ | |||||||
| #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> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <deque> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
| @@ -86,7 +88,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 +111,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; | ||||||
| @@ -150,7 +157,7 @@ namespace ix | |||||||
|         // Contains all messages that were fetched in the last socket read. |         // Contains all messages that were fetched in the last socket read. | ||||||
|         // This could be a mix of control messages (Close, Ping, etc...) and |         // This could be a mix of control messages (Close, Ping, etc...) and | ||||||
|         // data messages. That buffer is resized |         // data messages. That buffer is resized | ||||||
|         std::vector<uint8_t> _rxbuf; |         std::deque<uint8_t> _rxbuf; | ||||||
|  |  | ||||||
|         // Contains all messages that are waiting to be sent |         // Contains all messages that are waiting to be sent | ||||||
|         std::vector<uint8_t> _txbuf; |         std::vector<uint8_t> _txbuf; | ||||||
| @@ -214,7 +221,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.1" | #define IX_WEBSOCKET_VERSION "11.4.3" | ||||||
|   | |||||||
							
								
								
									
										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----- | ||||||
| @@ -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; | ||||||
|   | |||||||
							
								
								
									
										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