Compare commits
	
		
			24 Commits
		
	
	
		
			v11.4.2
			...
			feature/or
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4027f9282b | ||
| 
						 | 
					eb9a7bed76 | ||
| 
						 | 
					d20864d7d1 | ||
| 
						 | 
					f184a7adef | ||
| 
						 | 
					dc7b986e10 | ||
| 
						 | 
					1e3560014f | ||
| 
						 | 
					9157873f5b | ||
| 
						 | 
					aa2ca19895 | ||
| 
						 | 
					6cc21f3658 | ||
| 
						 | 
					679ce519dd | ||
| 
						 | 
					a5d4911a16 | ||
| 
						 | 
					b0fd119d14 | ||
| 
						 | 
					472cf68c31 | ||
| 
						 | 
					1e46466114 | ||
| 
						 | 
					0b8b5608dc | ||
| 
						 | 
					20a028e2ae | ||
| 
						 | 
					8d7b557be6 | ||
| 
						 | 
					e417e63605 | ||
| 
						 | 
					7b1524d7ec | ||
| 
						 | 
					e8048ad826 | ||
| 
						 | 
					2b40a30c8f | ||
| 
						 | 
					d7bfe89e43 | ||
| 
						 | 
					84aa652846 | ||
| 
						 | 
					edb6ded99f | 
							
								
								
									
										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)
 | 
				
			||||||
@@ -285,8 +287,8 @@ if (IXWEBSOCKET_INSTALL)
 | 
				
			|||||||
          PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/
 | 
					          PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY)
 | 
					  configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY)
 | 
				
			||||||
  install(FILES "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket")
 | 
					  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket")
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  install(EXPORT ixwebsocket
 | 
					  install(EXPORT ixwebsocket
 | 
				
			||||||
          FILE ixwebsocket-targets.cmake
 | 
					          FILE ixwebsocket-targets.cmake
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
@@ -202,6 +209,12 @@ namespace ix
 | 
				
			|||||||
            ss << "User-Agent: " << userAgent() << "\r\n";
 | 
					            ss << "User-Agent: " << userAgent() << "\r\n";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Set an origin header if missing
 | 
				
			||||||
 | 
					        if (args->extraHeaders.find("Origin") == args->extraHeaders.end())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
 | 
					        if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // Set request compression header
 | 
					            // Set request compression header
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@
 | 
				
			|||||||
#include "IXNetSystem.h"
 | 
					#include "IXNetSystem.h"
 | 
				
			||||||
#include "IXSocketConnect.h"
 | 
					#include "IXSocketConnect.h"
 | 
				
			||||||
#include "IXUserAgent.h"
 | 
					#include "IXUserAgent.h"
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <cstring>
 | 
					#include <cstring>
 | 
				
			||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
@@ -40,6 +41,29 @@ namespace
 | 
				
			|||||||
        auto vec = res.second;
 | 
					        auto vec = res.second;
 | 
				
			||||||
        return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
 | 
					        return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string response_head_file(const std::string& file_name){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (std::string::npos != file_name.find(".html") || std::string::npos != file_name.find(".htm"))
 | 
				
			||||||
 | 
					            return "text/html";
 | 
				
			||||||
 | 
					        else if (std::string::npos != file_name.find(".css"))
 | 
				
			||||||
 | 
					            return "text/css";
 | 
				
			||||||
 | 
					        else if (std::string::npos != file_name.find(".js") || std::string::npos != file_name.find(".mjs"))
 | 
				
			||||||
 | 
					            return "application/x-javascript";
 | 
				
			||||||
 | 
					        else if (std::string::npos != file_name.find(".ico"))
 | 
				
			||||||
 | 
					            return "image/x-icon";
 | 
				
			||||||
 | 
					        else if (std::string::npos != file_name.find(".png"))
 | 
				
			||||||
 | 
					            return "image/png";
 | 
				
			||||||
 | 
					        else if (std::string::npos != file_name.find(".jpg") || std::string::npos != file_name.find(".jpeg"))
 | 
				
			||||||
 | 
					            return "image/jpeg";
 | 
				
			||||||
 | 
					        else if (std::string::npos != file_name.find(".gif"))
 | 
				
			||||||
 | 
					            return "image/gif";
 | 
				
			||||||
 | 
					        else if (std::string::npos != file_name.find(".svg"))
 | 
				
			||||||
 | 
					            return "image/svg+xml";
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return "application/octet-stream";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
@@ -51,28 +75,14 @@ namespace ix
 | 
				
			|||||||
                           int backlog,
 | 
					                           int backlog,
 | 
				
			||||||
                           size_t maxConnections,
 | 
					                           size_t maxConnections,
 | 
				
			||||||
                           int addressFamily,
 | 
					                           int addressFamily,
 | 
				
			||||||
                           int timeoutSecs)
 | 
					                           int timeoutSecs,
 | 
				
			||||||
        : SocketServer(port, host, backlog, maxConnections, addressFamily)
 | 
					                           int handshakeTimeoutSecs)
 | 
				
			||||||
        , _connectedClientsCount(0)
 | 
					        : WebSocketServer(port, host, backlog, maxConnections, handshakeTimeoutSecs, addressFamily)
 | 
				
			||||||
        , _timeoutSecs(timeoutSecs)
 | 
					        , _timeoutSecs(timeoutSecs)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        setDefaultConnectionCallback();
 | 
					        setDefaultConnectionCallback();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpServer::~HttpServer()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        stop();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void HttpServer::stop()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        stopAcceptingConnections();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // FIXME: cancelling / closing active clients ...
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        SocketServer::stop();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void HttpServer::setOnConnectionCallback(const OnConnectionCallback& callback)
 | 
					    void HttpServer::setOnConnectionCallback(const OnConnectionCallback& callback)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _onConnectionCallback = callback;
 | 
					        _onConnectionCallback = callback;
 | 
				
			||||||
@@ -81,34 +91,35 @@ namespace ix
 | 
				
			|||||||
    void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
 | 
					    void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
 | 
				
			||||||
                                      std::shared_ptr<ConnectionState> connectionState)
 | 
					                                      std::shared_ptr<ConnectionState> connectionState)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _connectedClientsCount++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto ret = Http::parseRequest(socket, _timeoutSecs);
 | 
					        auto ret = Http::parseRequest(socket, _timeoutSecs);
 | 
				
			||||||
        // FIXME: handle errors in parseRequest
 | 
					        // FIXME: handle errors in parseRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (std::get<0>(ret))
 | 
					        if (std::get<0>(ret))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
 | 
					            auto request = std::get<2>(ret);
 | 
				
			||||||
            if (!Http::sendResponse(response, socket))
 | 
					            std::shared_ptr<ix::HttpResponse> response;
 | 
				
			||||||
 | 
					            if (request->headers["Upgrade"] == "websocket")
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                logError("Cannot send response");
 | 
					                WebSocketServer::handleUpgrade(std::move(socket), connectionState, request);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                auto response = _onConnectionCallback(request, connectionState);
 | 
				
			||||||
 | 
					                if (!Http::sendResponse(response, socket))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    logError("Cannot send response");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        connectionState->setTerminated();
 | 
					        connectionState->setTerminated();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        _connectedClientsCount--;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t HttpServer::getConnectedClientsCount()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return _connectedClientsCount;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void HttpServer::setDefaultConnectionCallback()
 | 
					    void HttpServer::setDefaultConnectionCallback()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        setOnConnectionCallback(
 | 
					        setOnConnectionCallback(
 | 
				
			||||||
            [this](HttpRequestPtr request,
 | 
					            [this](HttpRequestPtr request,
 | 
				
			||||||
                   std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
 | 
					                   std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                std::string uri(request->uri);
 | 
					                std::string uri(request->uri);
 | 
				
			||||||
                if (uri.empty() || uri == "/")
 | 
					                if (uri.empty() || uri == "/")
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -117,6 +128,7 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                WebSocketHttpHeaders headers;
 | 
					                WebSocketHttpHeaders headers;
 | 
				
			||||||
                headers["Server"] = userAgent();
 | 
					                headers["Server"] = userAgent();
 | 
				
			||||||
 | 
					                headers["Content-Type"] = response_head_file(uri);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                std::string path("." + uri);
 | 
					                std::string path("." + uri);
 | 
				
			||||||
                auto res = readAsString(path);
 | 
					                auto res = readAsString(path);
 | 
				
			||||||
@@ -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,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __FreeBSD__
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef _WIN32
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef WIN32_LEAN_AND_MEAN
 | 
					#ifndef WIN32_LEAN_AND_MEAN
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,8 +6,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,8 +5,8 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "IXSelectInterrupt.h"
 | 
					#include "IXSelectInterrupt.h"
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <deque>
 | 
					#include <deque>
 | 
				
			||||||
#ifdef _WIN32
 | 
					#ifdef _WIN32
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "IXSelectInterrupt.h"
 | 
					#include "IXSelectInterrupt.h"
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@
 | 
				
			|||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
#include <fcntl.h>
 | 
					#include <fcntl.h>
 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <atomic>
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <functional>
 | 
					#include <functional>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -205,7 +205,9 @@ namespace ix
 | 
				
			|||||||
                _sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
 | 
					                _sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
 | 
				
			||||||
            SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
 | 
					            SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
 | 
				
			||||||
            SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
 | 
					            SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
 | 
				
			||||||
            SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
 | 
					
 | 
				
			||||||
 | 
					            if (!_tlsOptions.disable_hostname_validation)
 | 
				
			||||||
 | 
					                SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (_tlsOptions.isPeerVerifyDisabled())
 | 
					            if (_tlsOptions.isPeerVerifyDisabled())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,7 +102,7 @@ namespace ix
 | 
				
			|||||||
        // First do DNS resolution
 | 
					        // First do DNS resolution
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
        auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
 | 
					        auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
 | 
				
			||||||
        struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
 | 
					        auto res = dnsLookup->resolve(errMsg, isCancellationRequested);
 | 
				
			||||||
        if (res == nullptr)
 | 
					        if (res == nullptr)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return -1;
 | 
					            return -1;
 | 
				
			||||||
@@ -112,7 +112,7 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // iterate through the records to find a working peer
 | 
					        // iterate through the records to find a working peer
 | 
				
			||||||
        struct addrinfo* address;
 | 
					        struct addrinfo* address;
 | 
				
			||||||
        for (address = res; address != nullptr; address = address->ai_next)
 | 
					        for (address = res.get(); address != nullptr; address = address->ai_next)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //
 | 
					            //
 | 
				
			||||||
            // Second try to connect to the remote host
 | 
					            // Second try to connect to the remote host
 | 
				
			||||||
@@ -124,7 +124,6 @@ namespace ix
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        freeaddrinfo(res);
 | 
					 | 
				
			||||||
        return sockfd;
 | 
					        return sockfd;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
#include "IXNetSystem.h"
 | 
					#include "IXNetSystem.h"
 | 
				
			||||||
#include "IXSocket.h"
 | 
					#include "IXSocket.h"
 | 
				
			||||||
#include "IXSocketConnect.h"
 | 
					#include "IXSocketConnect.h"
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef _WIN32
 | 
					#ifdef _WIN32
 | 
				
			||||||
@@ -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,10 +196,13 @@ namespace ix
 | 
				
			|||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
 | 
					        if (!_tlsOptions.disable_hostname_validation)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            errMsg = "SNI setup failed";
 | 
					            if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
 | 
				
			||||||
            return false;
 | 
					            {
 | 
				
			||||||
 | 
					                errMsg = "SNI setup failed";
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.)
 | 
				
			||||||
            X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection);
 | 
					            if (!_tlsOptions.disable_hostname_validation)
 | 
				
			||||||
            X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0);
 | 
					            {
 | 
				
			||||||
 | 
					                X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection);
 | 
				
			||||||
 | 
					                X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
            handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested);
 | 
					            handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -219,6 +219,10 @@ namespace ix
 | 
				
			|||||||
        if (_gcThread.joinable())
 | 
					        if (_gcThread.joinable())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _stopGc = true;
 | 
					            _stopGc = true;
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
 | 
				
			||||||
 | 
					                _canContinueGC = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            _conditionVariableGC.notify_one();
 | 
					            _conditionVariableGC.notify_one();
 | 
				
			||||||
            _gcThread.join();
 | 
					            _gcThread.join();
 | 
				
			||||||
            _stopGc = false;
 | 
					            _stopGc = false;
 | 
				
			||||||
@@ -268,7 +272,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 +432,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 (;;)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -445,7 +455,10 @@ namespace ix
 | 
				
			|||||||
            if (!_stopGc)
 | 
					            if (!_stopGc)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                std::unique_lock<std::mutex> lock(_conditionVariableMutexGC);
 | 
					                std::unique_lock<std::mutex> lock(_conditionVariableMutexGC);
 | 
				
			||||||
                _conditionVariableGC.wait(lock);
 | 
					                if(!_canContinueGC) {
 | 
				
			||||||
 | 
					                    _conditionVariableGC.wait(lock, [this]{ return _canContinueGC; });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _canContinueGC = false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -459,6 +472,10 @@ namespace ix
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        // a connection got terminated, we can run the connection thread GC,
 | 
					        // a connection got terminated, we can run the connection thread GC,
 | 
				
			||||||
        // so wake up the thread responsible for that
 | 
					        // so wake up the thread responsible for that
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
 | 
				
			||||||
 | 
					            _canContinueGC = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        _conditionVariableGC.notify_one();
 | 
					        _conditionVariableGC.notify_one();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -126,5 +126,6 @@ namespace ix
 | 
				
			|||||||
        // as a connection
 | 
					        // as a connection
 | 
				
			||||||
        std::condition_variable _conditionVariableGC;
 | 
					        std::condition_variable _conditionVariableGC;
 | 
				
			||||||
        std::mutex _conditionVariableMutexGC;
 | 
					        std::mutex _conditionVariableMutexGC;
 | 
				
			||||||
 | 
					        bool _canContinueGC{ false };
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,9 @@ namespace ix
 | 
				
			|||||||
        // whether tls is enabled, used for server code
 | 
					        // whether tls is enabled, used for server code
 | 
				
			||||||
        bool tls = false;
 | 
					        bool tls = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // whether to skip validating the peer's hostname against the certificate presented
 | 
				
			||||||
 | 
					        bool disable_hostname_validation = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool hasCertAndKey() const;
 | 
					        bool hasCertAndKey() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool isUsingSystemDefaults() const;
 | 
					        bool isUsingSystemDefaults() const;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -333,6 +333,19 @@ namespace
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return Result;
 | 
					        return Result;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int getProtocolPort(const std::string& protocol)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (protocol == "ws" || protocol == "http")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return 80;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (protocol == "wss" || protocol == "https")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return 443;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
} // namespace
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
@@ -343,6 +356,18 @@ namespace ix
 | 
				
			|||||||
                          std::string& path,
 | 
					                          std::string& path,
 | 
				
			||||||
                          std::string& query,
 | 
					                          std::string& query,
 | 
				
			||||||
                          int& port)
 | 
					                          int& port)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        bool isProtocolDefaultPort;
 | 
				
			||||||
 | 
					        return parse(url, protocol, host, path, query, port, isProtocolDefaultPort);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool UrlParser::parse(const std::string& url,
 | 
				
			||||||
 | 
					                              std::string& protocol,
 | 
				
			||||||
 | 
					                              std::string& host,
 | 
				
			||||||
 | 
					                              std::string& path,
 | 
				
			||||||
 | 
					                              std::string& query,
 | 
				
			||||||
 | 
					                              int& port,
 | 
				
			||||||
 | 
					                              bool& isProtocolDefaultPort)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        clParseURL res = clParseURL::ParseURL(url);
 | 
					        clParseURL res = clParseURL::ParseURL(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -356,23 +381,12 @@ namespace ix
 | 
				
			|||||||
        path = res.m_Path;
 | 
					        path = res.m_Path;
 | 
				
			||||||
        query = res.m_Query;
 | 
					        query = res.m_Query;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const auto protocolPort = getProtocolPort(protocol);
 | 
				
			||||||
        if (!res.GetPort(&port))
 | 
					        if (!res.GetPort(&port))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (protocol == "ws" || protocol == "http")
 | 
					            port = protocolPort;
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                port = 80;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (protocol == "wss" || protocol == "https")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                port = 443;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Invalid protocol. Should be caught by regex check
 | 
					 | 
				
			||||||
                // but this missing branch trigger cpplint linter.
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        isProtocolDefaultPort = port == protocolPort;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (path.empty())
 | 
					        if (path.empty())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,5 +19,13 @@ namespace ix
 | 
				
			|||||||
                          std::string& path,
 | 
					                          std::string& path,
 | 
				
			||||||
                          std::string& query,
 | 
					                          std::string& query,
 | 
				
			||||||
                          int& port);
 | 
					                          int& port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static bool parse(const std::string& url,
 | 
				
			||||||
 | 
					                          std::string& protocol,
 | 
				
			||||||
 | 
					                          std::string& host,
 | 
				
			||||||
 | 
					                          std::string& path,
 | 
				
			||||||
 | 
					                          std::string& query,
 | 
				
			||||||
 | 
					                          int& port,
 | 
				
			||||||
 | 
					                          bool& isProtocolDefaultPort);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "IXUuid.h"
 | 
					#include "IXUuid.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <iomanip>
 | 
					#include <iomanip>
 | 
				
			||||||
#include <random>
 | 
					#include <random>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@
 | 
				
			|||||||
#include "IXWebSocketHandshake.h"
 | 
					#include "IXWebSocketHandshake.h"
 | 
				
			||||||
#include <cassert>
 | 
					#include <cassert>
 | 
				
			||||||
#include <cmath>
 | 
					#include <cmath>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace
 | 
					namespace
 | 
				
			||||||
@@ -39,9 +40,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,8 +399,9 @@ namespace ix
 | 
				
			|||||||
                [this](const std::string& msg,
 | 
					                [this](const std::string& msg,
 | 
				
			||||||
                       size_t wireSize,
 | 
					                       size_t wireSize,
 | 
				
			||||||
                       bool decompressionError,
 | 
					                       bool decompressionError,
 | 
				
			||||||
                       WebSocketTransport::MessageKind messageKind) {
 | 
					                       WebSocketTransport::MessageKind messageKind)
 | 
				
			||||||
                    WebSocketMessageType webSocketMessageType{WebSocketMessageType::Error};
 | 
					                {
 | 
				
			||||||
 | 
					                    WebSocketMessageType webSocketMessageType {WebSocketMessageType::Error};
 | 
				
			||||||
                    switch (messageKind)
 | 
					                    switch (messageKind)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        case WebSocketTransport::MessageKind::MSG_TEXT:
 | 
					                        case WebSocketTransport::MessageKind::MSG_TEXT:
 | 
				
			||||||
@@ -503,13 +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(
 | 
				
			||||||
@@ -87,6 +87,7 @@ namespace ix
 | 
				
			|||||||
    WebSocketInitResult WebSocketHandshake::clientHandshake(
 | 
					    WebSocketInitResult WebSocketHandshake::clientHandshake(
 | 
				
			||||||
        const std::string& url,
 | 
					        const std::string& url,
 | 
				
			||||||
        const WebSocketHttpHeaders& extraHeaders,
 | 
					        const WebSocketHttpHeaders& extraHeaders,
 | 
				
			||||||
 | 
					        const std::string& protocol,
 | 
				
			||||||
        const std::string& host,
 | 
					        const std::string& host,
 | 
				
			||||||
        const std::string& path,
 | 
					        const std::string& path,
 | 
				
			||||||
        int port,
 | 
					        int port,
 | 
				
			||||||
@@ -106,15 +107,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";
 | 
				
			||||||
@@ -130,6 +126,12 @@ namespace ix
 | 
				
			|||||||
            ss << "User-Agent: " << userAgent() << "\r\n";
 | 
					            ss << "User-Agent: " << userAgent() << "\r\n";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Set an origin header if missing
 | 
				
			||||||
 | 
					        if (extraHeaders.find("Origin") == extraHeaders.end())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (auto& it : extraHeaders)
 | 
					        for (auto& it : extraHeaders)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ss << it.first << ": " << it.second << "\r\n";
 | 
					            ss << it.first << ": " << it.second << "\r\n";
 | 
				
			||||||
@@ -245,28 +247,42 @@ namespace ix
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs,
 | 
					    WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs,
 | 
				
			||||||
                                                            bool enablePerMessageDeflate)
 | 
					                                                            bool enablePerMessageDeflate,
 | 
				
			||||||
 | 
					                                                            HttpRequestPtr request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _requestInitCancellation = false;
 | 
					        _requestInitCancellation = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto isCancellationRequested =
 | 
					        auto isCancellationRequested =
 | 
				
			||||||
            makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
 | 
					            makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Read first line
 | 
					        std::string method;
 | 
				
			||||||
        auto lineResult = _socket->readLine(isCancellationRequested);
 | 
					        std::string uri;
 | 
				
			||||||
        auto lineValid = lineResult.first;
 | 
					        std::string httpVersion;
 | 
				
			||||||
        auto line = lineResult.second;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!lineValid)
 | 
					        if (request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return sendErrorResponse(400, "Error reading HTTP request line");
 | 
					            method = request->method;
 | 
				
			||||||
 | 
					            uri = request->uri;
 | 
				
			||||||
 | 
					            httpVersion = request->version;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Read first line
 | 
				
			||||||
 | 
					            auto lineResult = _socket->readLine(isCancellationRequested);
 | 
				
			||||||
 | 
					            auto lineValid = lineResult.first;
 | 
				
			||||||
 | 
					            auto line = lineResult.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Validate request line (GET /foo HTTP/1.1\r\n)
 | 
					            if (!lineValid)
 | 
				
			||||||
        auto requestLine = Http::parseRequestLine(line);
 | 
					            {
 | 
				
			||||||
        auto method = std::get<0>(requestLine);
 | 
					                return sendErrorResponse(400, "Error reading HTTP request line");
 | 
				
			||||||
        auto uri = std::get<1>(requestLine);
 | 
					            }
 | 
				
			||||||
        auto httpVersion = std::get<2>(requestLine);
 | 
					
 | 
				
			||||||
 | 
					            // Validate request line (GET /foo HTTP/1.1\r\n)
 | 
				
			||||||
 | 
					            auto requestLine = Http::parseRequestLine(line);
 | 
				
			||||||
 | 
					            method = std::get<0>(requestLine);
 | 
				
			||||||
 | 
					            uri = std::get<1>(requestLine);
 | 
				
			||||||
 | 
					            httpVersion = std::get<2>(requestLine);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (method != "GET")
 | 
					        if (method != "GET")
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -279,14 +295,22 @@ namespace ix
 | 
				
			|||||||
                                     "Invalid HTTP version, need HTTP/1.1, got: " + httpVersion);
 | 
					                                     "Invalid HTTP version, need HTTP/1.1, got: " + httpVersion);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Retrieve and validate HTTP headers
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
        auto result = parseHttpHeaders(_socket, isCancellationRequested);
 | 
					        if (request)
 | 
				
			||||||
        auto headersValid = result.first;
 | 
					 | 
				
			||||||
        auto headers = result.second;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!headersValid)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return sendErrorResponse(400, "Error parsing HTTP headers");
 | 
					            headers = request->headers;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Retrieve and validate HTTP headers
 | 
				
			||||||
 | 
					            auto result = parseHttpHeaders(_socket, isCancellationRequested);
 | 
				
			||||||
 | 
					            auto headersValid = result.first;
 | 
				
			||||||
 | 
					            headers = result.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!headersValid)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return sendErrorResponse(400, "Error parsing HTTP headers");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (headers.find("sec-websocket-key") == headers.end())
 | 
					        if (headers.find("sec-websocket-key") == headers.end())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "IXCancellationRequest.h"
 | 
					#include "IXCancellationRequest.h"
 | 
				
			||||||
 | 
					#include "IXHttp.h"
 | 
				
			||||||
#include "IXSocket.h"
 | 
					#include "IXSocket.h"
 | 
				
			||||||
#include "IXWebSocketHttpHeaders.h"
 | 
					#include "IXWebSocketHttpHeaders.h"
 | 
				
			||||||
#include "IXWebSocketInitResult.h"
 | 
					#include "IXWebSocketInitResult.h"
 | 
				
			||||||
@@ -30,12 +31,15 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        WebSocketInitResult clientHandshake(const std::string& url,
 | 
					        WebSocketInitResult clientHandshake(const std::string& url,
 | 
				
			||||||
                                            const WebSocketHttpHeaders& extraHeaders,
 | 
					                                            const WebSocketHttpHeaders& extraHeaders,
 | 
				
			||||||
 | 
					                                            const std::string& protocol,
 | 
				
			||||||
                                            const std::string& host,
 | 
					                                            const std::string& host,
 | 
				
			||||||
                                            const std::string& path,
 | 
					                                            const std::string& path,
 | 
				
			||||||
                                            int port,
 | 
					                                            int port,
 | 
				
			||||||
                                            int timeoutSecs);
 | 
					                                            int timeoutSecs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate);
 | 
					        WebSocketInitResult serverHandshake(int timeoutSecs,
 | 
				
			||||||
 | 
					                                            bool enablePerMessageDeflate,
 | 
				
			||||||
 | 
					                                            HttpRequestPtr request = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        std::string genRandomString(const int len);
 | 
					        std::string genRandomString(const int len);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,8 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "IXWebSocketPerMessageDeflate.h"
 | 
					#include "IXWebSocketPerMessageDeflate.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "IXUniquePtr.h"
 | 
					#include "IXUniquePtr.h"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@
 | 
				
			|||||||
#include "zlib.h"
 | 
					#include "zlib.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include "IXWebSocketSendData.h"
 | 
					#include "IXWebSocketSendData.h"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,28 +182,30 @@ namespace ix
 | 
				
			|||||||
    //
 | 
					    //
 | 
				
			||||||
    void WebSocketServer::makeBroadcastServer()
 | 
					    void WebSocketServer::makeBroadcastServer()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        setOnClientMessageCallback([this](std::shared_ptr<ConnectionState> connectionState,
 | 
					        setOnClientMessageCallback(
 | 
				
			||||||
                                          WebSocket& webSocket,
 | 
					            [this](std::shared_ptr<ConnectionState> connectionState,
 | 
				
			||||||
                                          const WebSocketMessagePtr& msg) {
 | 
					                   WebSocket& webSocket,
 | 
				
			||||||
            auto remoteIp = connectionState->getRemoteIp();
 | 
					                   const WebSocketMessagePtr& msg)
 | 
				
			||||||
            if (msg->type == ix::WebSocketMessageType::Message)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                for (auto&& client : getClients())
 | 
					                auto remoteIp = connectionState->getRemoteIp();
 | 
				
			||||||
 | 
					                if (msg->type == ix::WebSocketMessageType::Message)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (client.get() != &webSocket)
 | 
					                    for (auto&& client : getClients())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        client->send(msg->str, msg->binary);
 | 
					                        if (client.get() != &webSocket)
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // Make sure the OS send buffer is flushed before moving on
 | 
					 | 
				
			||||||
                        do
 | 
					 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            std::chrono::duration<double, std::milli> duration(500);
 | 
					                            client->send(msg->str, msg->binary);
 | 
				
			||||||
                            std::this_thread::sleep_for(duration);
 | 
					
 | 
				
			||||||
                        } while (client->bufferedAmount() != 0);
 | 
					                            // Make sure the OS send buffer is flushed before moving on
 | 
				
			||||||
 | 
					                            do
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                std::chrono::duration<double, std::milli> duration(500);
 | 
				
			||||||
 | 
					                                std::this_thread::sleep_for(duration);
 | 
				
			||||||
 | 
					                            } while (client->bufferedAmount() != 0);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            });
 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketServer::listenAndStart()
 | 
					    bool WebSocketServer::listenAndStart()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,6 +55,7 @@ namespace ix
 | 
				
			|||||||
        int getHandshakeTimeoutSecs();
 | 
					        int getHandshakeTimeoutSecs();
 | 
				
			||||||
        bool isPongEnabled();
 | 
					        bool isPongEnabled();
 | 
				
			||||||
        bool isPerMessageDeflateEnabled();
 | 
					        bool isPerMessageDeflateEnabled();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        // Member variables
 | 
					        // Member variables
 | 
				
			||||||
        int _handshakeTimeoutSecs;
 | 
					        int _handshakeTimeoutSecs;
 | 
				
			||||||
@@ -73,5 +74,10 @@ namespace ix
 | 
				
			|||||||
        virtual void handleConnection(std::unique_ptr<Socket> socket,
 | 
					        virtual void handleConnection(std::unique_ptr<Socket> socket,
 | 
				
			||||||
                                      std::shared_ptr<ConnectionState> connectionState);
 | 
					                                      std::shared_ptr<ConnectionState> connectionState);
 | 
				
			||||||
        virtual size_t getConnectedClientsCount() final;
 | 
					        virtual size_t getConnectedClientsCount() final;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected:
 | 
				
			||||||
 | 
					        void handleUpgrade(std::unique_ptr<Socket> socket,
 | 
				
			||||||
 | 
					                           std::shared_ptr<ConnectionState> connectionState,
 | 
				
			||||||
 | 
					                           HttpRequestPtr request = nullptr);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,7 +45,6 @@
 | 
				
			|||||||
#include <cstdarg>
 | 
					#include <cstdarg>
 | 
				
			||||||
#include <cstdlib>
 | 
					#include <cstdlib>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <thread>
 | 
					#include <thread>
 | 
				
			||||||
@@ -54,7 +53,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const std::string WebSocketTransport::kPingMessage("ixwebsocket::heartbeat");
 | 
					 | 
				
			||||||
    const int WebSocketTransport::kDefaultPingIntervalSecs(-1);
 | 
					    const int WebSocketTransport::kDefaultPingIntervalSecs(-1);
 | 
				
			||||||
    const bool WebSocketTransport::kDefaultEnablePong(true);
 | 
					    const bool WebSocketTransport::kDefaultEnablePong(true);
 | 
				
			||||||
    const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300);
 | 
					    const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300);
 | 
				
			||||||
@@ -74,6 +72,9 @@ namespace ix
 | 
				
			|||||||
        , _enablePong(kDefaultEnablePong)
 | 
					        , _enablePong(kDefaultEnablePong)
 | 
				
			||||||
        , _pingIntervalSecs(kDefaultPingIntervalSecs)
 | 
					        , _pingIntervalSecs(kDefaultPingIntervalSecs)
 | 
				
			||||||
        , _pongReceived(false)
 | 
					        , _pongReceived(false)
 | 
				
			||||||
 | 
					        , _setCustomMessage(false)
 | 
				
			||||||
 | 
					        , _kPingMessage("ixwebsocket::heartbeat")
 | 
				
			||||||
 | 
					        , _pingType(SendMessageKind::Ping)
 | 
				
			||||||
        , _pingCount(0)
 | 
					        , _pingCount(0)
 | 
				
			||||||
        , _lastSendPingTimePoint(std::chrono::steady_clock::now())
 | 
					        , _lastSendPingTimePoint(std::chrono::steady_clock::now())
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -139,7 +140,7 @@ namespace ix
 | 
				
			|||||||
                                                  _enablePerMessageDeflate);
 | 
					                                                  _enablePerMessageDeflate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            result = webSocketHandshake.clientHandshake(
 | 
					            result = webSocketHandshake.clientHandshake(
 | 
				
			||||||
                remoteUrl, headers, host, path, port, timeoutSecs);
 | 
					                remoteUrl, headers, protocol, host, path, port, timeoutSecs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result.http_status >= 300 && result.http_status < 400)
 | 
					            if (result.http_status >= 300 && result.http_status < 400)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -170,7 +171,8 @@ namespace ix
 | 
				
			|||||||
    // Server
 | 
					    // Server
 | 
				
			||||||
    WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket,
 | 
					    WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket,
 | 
				
			||||||
                                                            int timeoutSecs,
 | 
					                                                            int timeoutSecs,
 | 
				
			||||||
                                                            bool enablePerMessageDeflate)
 | 
					                                                            bool enablePerMessageDeflate,
 | 
				
			||||||
 | 
					                                                            HttpRequestPtr request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::lock_guard<std::mutex> lock(_socketMutex);
 | 
					        std::lock_guard<std::mutex> lock(_socketMutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -187,7 +189,8 @@ namespace ix
 | 
				
			|||||||
                                              _perMessageDeflateOptions,
 | 
					                                              _perMessageDeflateOptions,
 | 
				
			||||||
                                              _enablePerMessageDeflate);
 | 
					                                              _enablePerMessageDeflate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate);
 | 
					        auto result =
 | 
				
			||||||
 | 
					            webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate, request);
 | 
				
			||||||
        if (result.success)
 | 
					        if (result.success)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            setReadyState(ReadyState::OPEN);
 | 
					            setReadyState(ReadyState::OPEN);
 | 
				
			||||||
@@ -248,13 +251,51 @@ namespace ix
 | 
				
			|||||||
        return now - _lastSendPingTimePoint > std::chrono::seconds(_pingIntervalSecs);
 | 
					        return now - _lastSendPingTimePoint > std::chrono::seconds(_pingIntervalSecs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WebSocketSendInfo WebSocketTransport::sendHeartBeat()
 | 
					    void WebSocketTransport::setPingMessage(const std::string& message, SendMessageKind pingType)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _setCustomMessage = true;
 | 
				
			||||||
 | 
					        _kPingMessage = message;
 | 
				
			||||||
 | 
					        _pingType = pingType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    WebSocketSendInfo WebSocketTransport::sendHeartBeat(SendMessageKind pingMessage)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _pongReceived = false;
 | 
					        _pongReceived = false;
 | 
				
			||||||
        std::stringstream ss;
 | 
					        std::stringstream ss;
 | 
				
			||||||
        ss << kPingMessage << "::" << _pingIntervalSecs << "s"
 | 
					
 | 
				
			||||||
           << "::" << _pingCount++;
 | 
					        ss << _kPingMessage;
 | 
				
			||||||
        return sendPing(ss.str());
 | 
					        if (!_setCustomMessage)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << "::" << _pingIntervalSecs << "s"
 | 
				
			||||||
 | 
					               << "::" << _pingCount++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (pingMessage == SendMessageKind::Ping)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return sendPing(ss.str());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (pingMessage == SendMessageKind::Binary)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            WebSocketSendInfo info = sendBinary(ss.str(), nullptr);
 | 
				
			||||||
 | 
					            if (info.success)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::lock_guard<std::mutex> lck(_lastSendPingTimePointMutex);
 | 
				
			||||||
 | 
					                _lastSendPingTimePoint = std::chrono::steady_clock::now();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return info;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (pingMessage == SendMessageKind::Text)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            WebSocketSendInfo info = sendText(ss.str(), nullptr);
 | 
				
			||||||
 | 
					            if (info.success)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::lock_guard<std::mutex> lck(_lastSendPingTimePointMutex);
 | 
				
			||||||
 | 
					                _lastSendPingTimePoint = std::chrono::steady_clock::now();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return info;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // unknow type ping message
 | 
				
			||||||
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketTransport::closingDelayExceeded()
 | 
					    bool WebSocketTransport::closingDelayExceeded()
 | 
				
			||||||
@@ -270,7 +311,9 @@ namespace ix
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (pingIntervalExceeded())
 | 
					            if (pingIntervalExceeded())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!_pongReceived)
 | 
					                // If it is not a 'ping' message of ping type, there is no need to judge whether
 | 
				
			||||||
 | 
					                // pong will receive it
 | 
				
			||||||
 | 
					                if (_pingType == SendMessageKind::Ping && !_pongReceived)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // ping response (PONG) exceeds the maximum delay, close the connection
 | 
					                    // ping response (PONG) exceeds the maximum delay, close the connection
 | 
				
			||||||
                    close(WebSocketCloseConstants::kInternalErrorCode,
 | 
					                    close(WebSocketCloseConstants::kInternalErrorCode,
 | 
				
			||||||
@@ -278,7 +321,7 @@ namespace ix
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    sendHeartBeat();
 | 
					                    sendHeartBeat(_pingType);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -657,6 +700,7 @@ namespace ix
 | 
				
			|||||||
                if (_readyState != ReadyState::CLOSING)
 | 
					                if (_readyState != ReadyState::CLOSING)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // send back the CLOSE frame
 | 
					                    // send back the CLOSE frame
 | 
				
			||||||
 | 
					                    setReadyState(ReadyState::CLOSING);
 | 
				
			||||||
                    sendCloseFrame(code, reason);
 | 
					                    sendCloseFrame(code, reason);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    wakeUpFromPoll(SelectInterrupt::kCloseRequest);
 | 
					                    wakeUpFromPoll(SelectInterrupt::kCloseRequest);
 | 
				
			||||||
@@ -1029,7 +1073,10 @@ namespace ix
 | 
				
			|||||||
            else if (ret <= 0)
 | 
					            else if (ret <= 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                closeSocket();
 | 
					                closeSocket();
 | 
				
			||||||
                setReadyState(ReadyState::CLOSED);
 | 
					                if (_readyState != ReadyState::CLOSING)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    setReadyState(ReadyState::CLOSED);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,9 +18,10 @@
 | 
				
			|||||||
#include "IXWebSocketHttpHeaders.h"
 | 
					#include "IXWebSocketHttpHeaders.h"
 | 
				
			||||||
#include "IXWebSocketPerMessageDeflate.h"
 | 
					#include "IXWebSocketPerMessageDeflate.h"
 | 
				
			||||||
#include "IXWebSocketPerMessageDeflateOptions.h"
 | 
					#include "IXWebSocketPerMessageDeflateOptions.h"
 | 
				
			||||||
#include "IXWebSocketSendInfo.h"
 | 
					 | 
				
			||||||
#include "IXWebSocketSendData.h"
 | 
					#include "IXWebSocketSendData.h"
 | 
				
			||||||
 | 
					#include "IXWebSocketSendInfo.h"
 | 
				
			||||||
#include <atomic>
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <functional>
 | 
					#include <functional>
 | 
				
			||||||
#include <list>
 | 
					#include <list>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
@@ -86,7 +87,8 @@ namespace ix
 | 
				
			|||||||
        // Server
 | 
					        // Server
 | 
				
			||||||
        WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket,
 | 
					        WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket,
 | 
				
			||||||
                                            int timeoutSecs,
 | 
					                                            int timeoutSecs,
 | 
				
			||||||
                                            bool enablePerMessageDeflate);
 | 
					                                            bool enablePerMessageDeflate,
 | 
				
			||||||
 | 
					                                            HttpRequestPtr request = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        PollResult poll();
 | 
					        PollResult poll();
 | 
				
			||||||
        WebSocketSendInfo sendBinary(const IXWebSocketSendData& message,
 | 
					        WebSocketSendInfo sendBinary(const IXWebSocketSendData& message,
 | 
				
			||||||
@@ -108,8 +110,12 @@ namespace ix
 | 
				
			|||||||
        void dispatch(PollResult pollResult, const OnMessageCallback& onMessageCallback);
 | 
					        void dispatch(PollResult pollResult, const OnMessageCallback& onMessageCallback);
 | 
				
			||||||
        size_t bufferedAmount() const;
 | 
					        size_t bufferedAmount() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // set ping heartbeat message
 | 
				
			||||||
 | 
					        void setPingMessage(const std::string& message, SendMessageKind pingType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // internal
 | 
					        // internal
 | 
				
			||||||
        WebSocketSendInfo sendHeartBeat();
 | 
					        // send any type of ping packet, not only 'ping' type
 | 
				
			||||||
 | 
					        WebSocketSendInfo sendHeartBeat(SendMessageKind pingType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        std::string _url;
 | 
					        std::string _url;
 | 
				
			||||||
@@ -214,7 +220,10 @@ namespace ix
 | 
				
			|||||||
        std::atomic<bool> _pongReceived;
 | 
					        std::atomic<bool> _pongReceived;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        static const int kDefaultPingIntervalSecs;
 | 
					        static const int kDefaultPingIntervalSecs;
 | 
				
			||||||
        static const std::string kPingMessage;
 | 
					
 | 
				
			||||||
 | 
					        bool _setCustomMessage;
 | 
				
			||||||
 | 
					        std::string _kPingMessage;
 | 
				
			||||||
 | 
					        SendMessageKind _pingType;
 | 
				
			||||||
        std::atomic<uint64_t> _pingCount;
 | 
					        std::atomic<uint64_t> _pingCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // We record when ping are being sent so that we can know when to send the next one
 | 
					        // We record when ping are being sent so that we can know when to send the next one
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,4 +6,4 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define IX_WEBSOCKET_VERSION "11.4.2"
 | 
					#define IX_WEBSOCKET_VERSION "11.4.3"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,16 +13,24 @@ all: brew
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
install: brew
 | 
					install: brew
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-DCMAKE_INSTALL_PREFIX=/opt/homebrew
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Use -DCMAKE_INSTALL_PREFIX= to install into another location
 | 
					# Use -DCMAKE_INSTALL_PREFIX= to install into another location
 | 
				
			||||||
# on osx it is good practice to make /usr/local user writable
 | 
					# on osx it is good practice to make /usr/local user writable
 | 
				
			||||||
# sudo chown -R `whoami`/staff /usr/local
 | 
					# sudo chown -R `whoami`/staff /usr/local
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					# Those days (since Apple Silicon mac shipped), on macOS homebrew installs in /opt/homebrew, and /usr/local is readonly
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
 | 
					# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Default rule does not use python as that requires first time users to have Python3 installed
 | 
					# Default rule does not use python as that requires first time users to have Python3 installed
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
brew:
 | 
					brew:
 | 
				
			||||||
 | 
					ifeq ($(shell uname),Darwin)
 | 
				
			||||||
 | 
						mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/homebrew -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
 | 
						mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the
 | 
					# Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the
 | 
				
			||||||
# server side ?) and I can't work-around it easily, so we're using mbedtls on
 | 
					# server side ?) and I can't work-around it easily, so we're using mbedtls on
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								test/.certs/wrong-name-server-crt.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								test/.certs/wrong-name-server-crt.pem
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					-----BEGIN CERTIFICATE-----
 | 
				
			||||||
 | 
					MIIDNDCCAhwCFCl+O/rR8flqYKKvD0iwkucFwMaLMA0GCSqGSIb3DQEBCwUAMEEx
 | 
				
			||||||
 | 
					FDASBgNVBAoMC21hY2hpbmV6b25lMRQwEgYDVQQKDAtJWFdlYlNvY2tldDETMBEG
 | 
				
			||||||
 | 
					A1UEAwwKdHJ1c3RlZC1jYTAgFw0yMjA4MjMyMDM2MjVaGA80MjgxMDYwMTIwMzYy
 | 
				
			||||||
 | 
					NVowajELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMREwDwYDVQQHDAhCZXJrZWxl
 | 
				
			||||||
 | 
					eTEbMBkGA1UECgwSRHVtbXkgT3JnYW5pemF0aW9uMR4wHAYDVQQDDBVub3QuYS52
 | 
				
			||||||
 | 
					YWxpZC5ob3N0Lm5hbWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2
 | 
				
			||||||
 | 
					9N806IjCvA82zfk9CPNwaEHOygNDJSXaZ38YDSI4C+Wf2imnMxrLQKaoccHUi+9L
 | 
				
			||||||
 | 
					4rQN/hSCg+uTULQUZ0iyssGDaIh4IcAeoEcNlXYHTrgP+aAaby3q5zeZ80K3+6e4
 | 
				
			||||||
 | 
					rqcuBPV2lLszJu3XXwEKbDSxW3De0gc2N8m9DN8Lx7i70DRf0F4m6RIMFF/kHXwa
 | 
				
			||||||
 | 
					zZLeG7rZb4xL684lmmQsWtk5Z600CvrBtUE7fQ7bhy0QhSt66kdTSL8IKQrbWcGj
 | 
				
			||||||
 | 
					q0tggFlOqeyZSi73gqUiAIuGO8/tRgmp4HygNyC24jpOB5nObOPPJTUEf5Mk1Bum
 | 
				
			||||||
 | 
					kD5a+yL6YbVdhiaK17wbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKsLXGLfO1IZ
 | 
				
			||||||
 | 
					LbofGc7/TCQwRayR3RdG4864PBy97KfJWyizg7Wm4X4yPFRG+6q3X5NKW32Ew9lI
 | 
				
			||||||
 | 
					jXulXCTjWOiSxiG4Pk20uczkOhWVHFdnS9gZvykPC/ElxBKPalT6MMstZWxpElsk
 | 
				
			||||||
 | 
					rCDKXj4LkD0po8bZrjlgSZQQQk6XMRkoRai2GWLJqIjaNCSF8nqb1wM/1OE1tAwi
 | 
				
			||||||
 | 
					polO1eFMA24yypvlXcNrNXjqcQ7LaoQFQltmi/RV+uTk7EK2F2jgYVzJ/pe2ET0i
 | 
				
			||||||
 | 
					RIMbGZTlAiemDGL9SpMsxftG6fSmP6QqDqYExmmPlZMLprb2da/2kelWFA+VkvdG
 | 
				
			||||||
 | 
					zFrnIcyfMY4=
 | 
				
			||||||
 | 
					-----END CERTIFICATE-----
 | 
				
			||||||
							
								
								
									
										27
									
								
								test/.certs/wrong-name-server-key.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								test/.certs/wrong-name-server-key.pem
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					-----BEGIN RSA PRIVATE KEY-----
 | 
				
			||||||
 | 
					MIIEpAIBAAKCAQEAtvTfNOiIwrwPNs35PQjzcGhBzsoDQyUl2md/GA0iOAvln9op
 | 
				
			||||||
 | 
					pzMay0CmqHHB1IvvS+K0Df4UgoPrk1C0FGdIsrLBg2iIeCHAHqBHDZV2B064D/mg
 | 
				
			||||||
 | 
					Gm8t6uc3mfNCt/unuK6nLgT1dpS7Mybt118BCmw0sVtw3tIHNjfJvQzfC8e4u9A0
 | 
				
			||||||
 | 
					X9BeJukSDBRf5B18Gs2S3hu62W+MS+vOJZpkLFrZOWetNAr6wbVBO30O24ctEIUr
 | 
				
			||||||
 | 
					eupHU0i/CCkK21nBo6tLYIBZTqnsmUou94KlIgCLhjvP7UYJqeB8oDcgtuI6TgeZ
 | 
				
			||||||
 | 
					zmzjzyU1BH+TJNQbppA+Wvsi+mG1XYYmite8GwIDAQABAoIBAGRzAXG9EglI01mV
 | 
				
			||||||
 | 
					sPfvyCi5NRhiFXRyGtxU4pTD8TuwXHxtfV0NU/KwJlBpVLBrvBCAAbeE/qHB6D9T
 | 
				
			||||||
 | 
					metx4ZorRs/tPrAmZ6LpANnWa50LfUdYGK0qyZ0lIYPm6YS2KJnfWm6LznEyq60j
 | 
				
			||||||
 | 
					/IW45YthaXTO7aOI0OjVrG+dd4CxU1g1NtCQ9bdDMDjfXFVnoOifXIl8W22eRMoZ
 | 
				
			||||||
 | 
					LzCz+0sI0R0LenXCPT566de21F0NDkIK7NaQ1l5BMX4PA+RsN3cZlzyruA1woPKI
 | 
				
			||||||
 | 
					aBR2LQGNrBfDVGMATtUm89RpWAftb8FmXqYUsM7zAzTO6dViitiB7OFlw7Ax15YH
 | 
				
			||||||
 | 
					MTj5zGECgYEA35ocEEMfyahBN70bjyiqOHlzKwFjDl9DsUf8xqHsNhYAL+GrOK9A
 | 
				
			||||||
 | 
					8lN5ByzcnbV3TJtU4WYbPgQJld8gXFx4h3eS+SkA/ASkAdtgHfdMImZ1v7k3TIPf
 | 
				
			||||||
 | 
					DXOCsHzELsQY6OgiI572Nwzx/Tl+0dFwaOfLjU9iEmmqL667j1Y4NiMCgYEA0Xch
 | 
				
			||||||
 | 
					9K/vwZ1I9gM3ySvG40R2TRriC9Bf8jwrEWeRsWNvBcqtMMrgwAMsMCKDugSZR7n6
 | 
				
			||||||
 | 
					o3WZV6mpvYVLFx0b93v07i7EpFP27kMw3gLNBKX62snR9JbqwAMK7tktgLds5pKM
 | 
				
			||||||
 | 
					DvLHuAQ9XMMXMLX7WlSyhmtFeU7IDulTSHHqdakCgYEAywITCpy2xpKRK7bwx4gH
 | 
				
			||||||
 | 
					C6EQc/IdahYJ0nHmSL0IRY6x+sbrelp7H8ezcVVEs5bmylGYvc/DWgm2XjCnI9P8
 | 
				
			||||||
 | 
					xhlFAhw9PZJFCT6QRISaxfy6WSgi0cBEieTeubd9MmxtpT/khuyy5AZHyj0iLAL4
 | 
				
			||||||
 | 
					CPayMwjopIj0r7f3p8qC3HsCgYBmq2kmYVI4aZrIkv02CtIatYTy+DlSJxnQRvOp
 | 
				
			||||||
 | 
					PUWpWB6kDRrk7pxJIYT4NwKwG+7xvFQA6PR3hn7fmUUcGDWMEeMVGDFkho9ja+W4
 | 
				
			||||||
 | 
					/FB3dc/Gi+PwakS4RwWF20e1brLfNXeXICMKrHFTVYC5bIm+VgOHZW8RLa9bt7wN
 | 
				
			||||||
 | 
					p2CPuQKBgQCjW+rCODmMzEues/I143mYMDdZ1IschtWGiXBNrpkHm/DcZSutbacm
 | 
				
			||||||
 | 
					5C7Kwv44pfA90NHDTjuaIgRgfeUTawkrl8uPXEuj80mW72agf5oS8lJzD+2jibCj
 | 
				
			||||||
 | 
					Q4K52G+0LaTxHLZxufil28Rgja01c0mTcuLbhKtCgHl5EHP19wOKEg==
 | 
				
			||||||
 | 
					-----END RSA PRIVATE KEY-----
 | 
				
			||||||
@@ -24,14 +24,13 @@ set (TEST_TARGET_NAMES
 | 
				
			|||||||
  # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws
 | 
					  # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws
 | 
				
			||||||
  IXStrCaseCompareTest
 | 
					  IXStrCaseCompareTest
 | 
				
			||||||
  IXExponentialBackoffTest
 | 
					  IXExponentialBackoffTest
 | 
				
			||||||
 | 
					  IXWebSocketCloseTest
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Some unittest don't work on windows yet
 | 
					# Some unittest don't work on windows yet
 | 
				
			||||||
# Windows without TLS does not have hmac yet
 | 
					# Windows without TLS does not have hmac yet
 | 
				
			||||||
if (UNIX)
 | 
					if (UNIX)
 | 
				
			||||||
  list(APPEND TEST_TARGET_NAMES
 | 
					  list(APPEND TEST_TARGET_NAMES
 | 
				
			||||||
    IXWebSocketCloseTest
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Fail on Windows in CI probably because the pathing is wrong and
 | 
					    # Fail on Windows in CI probably because the pathing is wrong and
 | 
				
			||||||
    # some resource files cannot be found
 | 
					    # some resource files cannot be found
 | 
				
			||||||
    IXHttpServerTest
 | 
					    IXHttpServerTest
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,13 +19,9 @@ TEST_CASE("dns", "[net]")
 | 
				
			|||||||
        auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80);
 | 
					        auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string errMsg;
 | 
					        std::string errMsg;
 | 
				
			||||||
        struct addrinfo* res;
 | 
					        auto res = dnsLookup->resolve(errMsg, [] { return false; });
 | 
				
			||||||
 | 
					 | 
				
			||||||
        res = dnsLookup->resolve(errMsg, [] { return false; });
 | 
					 | 
				
			||||||
        std::cerr << "Error message: " << errMsg << std::endl;
 | 
					        std::cerr << "Error message: " << errMsg << std::endl;
 | 
				
			||||||
        REQUIRE(res != nullptr);
 | 
					        REQUIRE(res != nullptr);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        dnsLookup->release(res);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SECTION("Test resolving a non-existing hostname")
 | 
					    SECTION("Test resolving a non-existing hostname")
 | 
				
			||||||
@@ -33,7 +29,7 @@ TEST_CASE("dns", "[net]")
 | 
				
			|||||||
        auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
 | 
					        auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string errMsg;
 | 
					        std::string errMsg;
 | 
				
			||||||
        struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; });
 | 
					        auto res = dnsLookup->resolve(errMsg, [] { return false; });
 | 
				
			||||||
        std::cerr << "Error message: " << errMsg << std::endl;
 | 
					        std::cerr << "Error message: " << errMsg << std::endl;
 | 
				
			||||||
        REQUIRE(res == nullptr);
 | 
					        REQUIRE(res == nullptr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -44,7 +40,7 @@ TEST_CASE("dns", "[net]")
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        std::string errMsg;
 | 
					        std::string errMsg;
 | 
				
			||||||
        // The callback returning true means we are requesting cancellation
 | 
					        // The callback returning true means we are requesting cancellation
 | 
				
			||||||
        struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; });
 | 
					        auto res = dnsLookup->resolve(errMsg, [] { return true; });
 | 
				
			||||||
        std::cerr << "Error message: " << errMsg << std::endl;
 | 
					        std::cerr << "Error message: " << errMsg << std::endl;
 | 
				
			||||||
        REQUIRE(res == nullptr);
 | 
					        REQUIRE(res == nullptr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,9 @@
 | 
				
			|||||||
#include "catch.hpp"
 | 
					#include "catch.hpp"
 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXGetFreePort.h>
 | 
				
			||||||
#include <ixwebsocket/IXHttpClient.h>
 | 
					#include <ixwebsocket/IXHttpClient.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXHttpServer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace ix;
 | 
					using namespace ix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,6 +97,52 @@ TEST_CASE("http_client", "[http]")
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(IXWEBSOCKET_USE_TLS) && !defined(IXWEBSOCKET_USE_SECURE_TRANSPORT)
 | 
				
			||||||
 | 
					    SECTION("Disable hostname validation")
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static auto test_cert_with_wrong_name = [](bool validate_hostname)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            int port = getFreePort();
 | 
				
			||||||
 | 
					            ix::HttpServer server(port, "127.0.0.1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SocketTLSOptions tlsOptionsServer;
 | 
				
			||||||
 | 
					            tlsOptionsServer.tls = true;
 | 
				
			||||||
 | 
					            tlsOptionsServer.caFile = "NONE";
 | 
				
			||||||
 | 
					            tlsOptionsServer.certFile = "./.certs/wrong-name-server-crt.pem";
 | 
				
			||||||
 | 
					            tlsOptionsServer.keyFile = "./.certs/wrong-name-server-key.pem";
 | 
				
			||||||
 | 
					            server.setTLSOptions(tlsOptionsServer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto res = server.listen();
 | 
				
			||||||
 | 
					            REQUIRE(res.first);
 | 
				
			||||||
 | 
					            server.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            HttpClient httpClient;
 | 
				
			||||||
 | 
					            SocketTLSOptions tlsOptionsClient;
 | 
				
			||||||
 | 
					            tlsOptionsClient.caFile = "./.certs/trusted-ca-crt.pem";
 | 
				
			||||||
 | 
					            tlsOptionsClient.disable_hostname_validation = validate_hostname;
 | 
				
			||||||
 | 
					            httpClient.setTLSOptions(tlsOptionsClient);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::string url("https://localhost:" + std::to_string(port));
 | 
				
			||||||
 | 
					            auto args = httpClient.createRequest(url);
 | 
				
			||||||
 | 
					            args->connectTimeout = 10;
 | 
				
			||||||
 | 
					            args->transferTimeout = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto response = httpClient.get(url, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::cerr << "Status: " << response->statusCode << std::endl;
 | 
				
			||||||
 | 
					            std::cerr << "Error code: " << (int) response->errorCode << std::endl;
 | 
				
			||||||
 | 
					            std::cerr << "Error message: " << response->errorMsg << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            server.stop();
 | 
				
			||||||
 | 
					            return std::make_tuple(response->errorCode, response->statusCode);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        REQUIRE(test_cert_with_wrong_name(false) ==
 | 
				
			||||||
 | 
					                std::make_tuple(HttpErrorCode::CannotConnect, 0));
 | 
				
			||||||
 | 
					        REQUIRE(test_cert_with_wrong_name(true) == std::make_tuple(HttpErrorCode::Ok, 404));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SECTION("Async API, one call")
 | 
					    SECTION("Async API, one call")
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        bool async = true;
 | 
					        bool async = true;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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