Compare commits
	
		
			10 Commits
		
	
	
		
			feature/zl
			...
			feature/kq
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0834198e74 | ||
| 
						 | 
					22dd32d4e9 | ||
| 
						 | 
					b15c4189f5 | ||
| 
						 | 
					74d3278258 | ||
| 
						 | 
					831152b906 | ||
| 
						 | 
					7c81a98632 | ||
| 
						 | 
					6e47c62c06 | ||
| 
						 | 
					bcae7f326d | ||
| 
						 | 
					d719c41e31 | ||
| 
						 | 
					6f0307fb35 | 
							
								
								
									
										4
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							@@ -10,12 +10,10 @@ jobs:
 | 
				
			|||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
    - uses: actions/checkout@v1
 | 
					    - uses: actions/checkout@v1
 | 
				
			||||||
    - uses: seanmiddleditch/gha-setup-vsdevenv@master
 | 
					    - uses: seanmiddleditch/gha-setup-vsdevenv@master
 | 
				
			||||||
    - run: |
 | 
					 | 
				
			||||||
        vcpkg install zlib:x64-uwp
 | 
					 | 
				
			||||||
    - run: |
 | 
					    - run: |
 | 
				
			||||||
        mkdir build
 | 
					        mkdir build
 | 
				
			||||||
        cd build
 | 
					        cd build
 | 
				
			||||||
        cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 ..
 | 
					        cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 ..
 | 
				
			||||||
    - run: cmake --build build
 | 
					    - run: cmake --build build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							@@ -10,10 +10,11 @@ jobs:
 | 
				
			|||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
    - uses: actions/checkout@v1
 | 
					    - uses: actions/checkout@v1
 | 
				
			||||||
    - uses: seanmiddleditch/gha-setup-vsdevenv@master
 | 
					    - uses: seanmiddleditch/gha-setup-vsdevenv@master
 | 
				
			||||||
    - run: |
 | 
					 | 
				
			||||||
        vcpkg install zlib:x64-windows
 | 
					 | 
				
			||||||
    - run: |
 | 
					    - run: |
 | 
				
			||||||
        mkdir build
 | 
					        mkdir build
 | 
				
			||||||
        cd build
 | 
					        cd build
 | 
				
			||||||
        cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 ..
 | 
					        cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 ..
 | 
				
			||||||
    - run: cmake --build build
 | 
					    - run: cmake --build build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#- run: ../build/test/ixwebsocket_unittest.exe
 | 
				
			||||||
 | 
					# working-directory: test
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -190,11 +190,17 @@ if (USE_TLS)
 | 
				
			|||||||
  endif()
 | 
					  endif()
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					option(USE_ZLIB "Enable zlib support" TRUE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (USE_ZLIB)
 | 
				
			||||||
  # Use ZLIB_ROOT CMake variable if you need to use your own zlib
 | 
					  # Use ZLIB_ROOT CMake variable if you need to use your own zlib
 | 
				
			||||||
  find_package(ZLIB REQUIRED)
 | 
					  find_package(ZLIB REQUIRED)
 | 
				
			||||||
  include_directories(${ZLIB_INCLUDE_DIRS})
 | 
					  include_directories(${ZLIB_INCLUDE_DIRS})
 | 
				
			||||||
  target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
 | 
					  target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (WIN32)
 | 
					if (WIN32)
 | 
				
			||||||
  target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
 | 
					  target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
 | 
				
			||||||
  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 | 
					  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,23 @@
 | 
				
			|||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
All changes to this project will be documented in this file.
 | 
					All changes to this project will be documented in this file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [10.1.5] - 2020-08-02
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(ws) Add a new ws sub-command, push_server. This command runs a server which sends many messages in a loop to a websocket client. We can receive above 200,000 messages per second (cf #235).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [10.1.4] - 2020-08-02
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(ws) Add a new ws sub-command, echo_client. This command sends a message to an echo server, and send back to a server whatever message it does receive. When connecting to a local ws echo_server, on my MacBook Pro 2015 I can send/receive around 30,000 messages per second. (cf #235)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [10.1.3] - 2020-08-02
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(ws) ws echo_server. Add a -q option to only enable warning and error log levels. This is useful for bench-marking so that we do not print a lot of things on the console. (cf #235)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [10.1.2] - 2020-07-31
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(build) make using zlib optional, with the caveat that some http and websocket features are not available when zlib is absent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [10.1.1] - 2020-07-29
 | 
					## [10.1.1] - 2020-07-29
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(websocket client) onProgressCallback not called for short messages on a websocket (fix #233)
 | 
					(websocket client) onProgressCallback not called for short messages on a websocket (fix #233)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ There is a unittest which can be executed by typing `make test`.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Options for building:
 | 
					Options for building:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `-DUSE_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension
 | 
				
			||||||
* `-DUSE_TLS=1` will enable TLS support
 | 
					* `-DUSE_TLS=1` will enable TLS support
 | 
				
			||||||
* `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
 | 
					* `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
 | 
				
			||||||
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
 | 
					* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								docs/performance.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								docs/performance.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					## WebSocket Client performance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We will run a client and a server on the same machine, connecting to localhost. This bench is run on a MacBook Pro from 2015. We can receive over 200,000 (small) messages per second, another way to put it is that it takes 5 micro-second to receive and process one message. This is an indication about the minimal latency to receive messages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Receiving messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					By using the push_server ws sub-command, the server will send the same message in a loop to any connected client.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					ws push_server -q --send_msg 'yo'
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					By using the echo_client ws sub-command, with the -m (mute or no_send), we will display statistics on how many messages we can receive per second.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					$ ws echo_client -m ws://localhost:8008
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.284] [info] ws_echo_client: connected
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.284] [info] Uri: /
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.284] [info] Headers:
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.284] [info] Connection: Upgrade
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.284] [info] Sec-WebSocket-Accept: byy/pMK2d0PtRwExaaiOnXJTQHo=
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.284] [info] Server: ixwebsocket/10.1.4 macos ssl/SecureTransport zlib 1.2.11
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.284] [info] Upgrade: websocket
 | 
				
			||||||
 | 
					[2020-08-02 12:31:17.663] [info] messages received: 0 per second 2595307 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:18.668] [info] messages received: 79679 per second 2674986 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:19.668] [info] messages received: 207438 per second 2882424 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:20.673] [info] messages received: 209207 per second 3091631 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:21.676] [info] messages received: 216056 per second 3307687 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:22.680] [info] messages received: 214927 per second 3522614 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:23.684] [info] messages received: 216960 per second 3739574 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:24.688] [info] messages received: 215232 per second 3954806 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:25.691] [info] messages received: 212300 per second 4167106 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:26.694] [info] messages received: 212501 per second 4379607 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:27.699] [info] messages received: 212330 per second 4591937 total
 | 
				
			||||||
 | 
					[2020-08-02 12:31:28.702] [info] messages received: 216511 per second 4808448 total
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
@@ -16,7 +16,10 @@
 | 
				
			|||||||
#include <random>
 | 
					#include <random>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
#include <zlib.h>
 | 
					#include <zlib.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -174,11 +177,13 @@ namespace ix
 | 
				
			|||||||
        ss << verb << " " << path << " HTTP/1.1\r\n";
 | 
					        ss << verb << " " << path << " HTTP/1.1\r\n";
 | 
				
			||||||
        ss << "Host: " << host << "\r\n";
 | 
					        ss << "Host: " << host << "\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        if (args->compress)
 | 
					        if (args->compress)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ss << "Accept-Encoding: gzip"
 | 
					            ss << "Accept-Encoding: gzip"
 | 
				
			||||||
               << "\r\n";
 | 
					               << "\r\n";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Append extra headers
 | 
					        // Append extra headers
 | 
				
			||||||
        for (auto&& it : args->extraHeaders)
 | 
					        for (auto&& it : args->extraHeaders)
 | 
				
			||||||
@@ -495,6 +500,7 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        downloadSize = payload.size();
 | 
					        downloadSize = payload.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        // If the content was compressed with gzip, decode it
 | 
					        // If the content was compressed with gzip, decode it
 | 
				
			||||||
        if (headers["Content-Encoding"] == "gzip")
 | 
					        if (headers["Content-Encoding"] == "gzip")
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -513,6 +519,7 @@ namespace ix
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            payload = decompressedPayload;
 | 
					            payload = decompressedPayload;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return std::make_shared<HttpResponse>(code,
 | 
					        return std::make_shared<HttpResponse>(code,
 | 
				
			||||||
                                              description,
 | 
					                                              description,
 | 
				
			||||||
@@ -672,6 +679,7 @@ namespace ix
 | 
				
			|||||||
        return ss.str();
 | 
					        return ss.str();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
    bool HttpClient::gzipInflate(const std::string& in, std::string& out)
 | 
					    bool HttpClient::gzipInflate(const std::string& in, std::string& out)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        z_stream inflateState;
 | 
					        z_stream inflateState;
 | 
				
			||||||
@@ -716,6 +724,7 @@ namespace ix
 | 
				
			|||||||
        inflateEnd(&inflateState);
 | 
					        inflateEnd(&inflateState);
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
 | 
					    void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,9 @@ namespace ix
 | 
				
			|||||||
    private:
 | 
					    private:
 | 
				
			||||||
        void log(const std::string& msg, HttpRequestArgsPtr args);
 | 
					        void log(const std::string& msg, HttpRequestArgsPtr args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        bool gzipInflate(const std::string& in, std::string& out);
 | 
					        bool gzipInflate(const std::string& in, std::string& out);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Async API background thread runner
 | 
					        // Async API background thread runner
 | 
				
			||||||
        void run();
 | 
					        void run();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,10 @@
 | 
				
			|||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
#include <zlib.h>
 | 
					#include <zlib.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace
 | 
					namespace
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -41,6 +44,7 @@ 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()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
    std::string gzipCompress(const std::string& str)
 | 
					    std::string gzipCompress(const std::string& str)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        z_stream zs; // z_stream is zlib's control structure
 | 
					        z_stream zs; // z_stream is zlib's control structure
 | 
				
			||||||
@@ -83,6 +87,7 @@ namespace
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return outstring;
 | 
					        return outstring;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
} // namespace
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
@@ -168,12 +173,14 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                std::string content = res.second;
 | 
					                std::string content = res.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
                std::string acceptEncoding = request->headers["Accept-encoding"];
 | 
					                std::string acceptEncoding = request->headers["Accept-encoding"];
 | 
				
			||||||
                if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos)
 | 
					                if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    content = gzipCompress(content);
 | 
					                    content = gzipCompress(content);
 | 
				
			||||||
                    headers["Content-Encoding"] = "gzip";
 | 
					                    headers["Content-Encoding"] = "gzip";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Log request
 | 
					                // Log request
 | 
				
			||||||
                std::stringstream ss;
 | 
					                std::stringstream ss;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,19 +35,122 @@ namespace ix
 | 
				
			|||||||
        : _sockfd(fd)
 | 
					        : _sockfd(fd)
 | 
				
			||||||
        , _selectInterrupt(createSelectInterrupt())
 | 
					        , _selectInterrupt(createSelectInterrupt())
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ;
 | 
					#if defined(__APPLE__)
 | 
				
			||||||
 | 
					        _kqueuefd = kqueue();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Socket::~Socket()
 | 
					    Socket::~Socket()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        close();
 | 
					        close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(__APPLE__)
 | 
				
			||||||
 | 
					        ::close(_kqueuefd);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PollResultType Socket::poll(bool readyToRead,
 | 
					    PollResultType Socket::poll(bool readyToRead,
 | 
				
			||||||
                                int timeoutMs,
 | 
					                                int timeoutMs,
 | 
				
			||||||
                                int sockfd,
 | 
					                                int sockfd,
 | 
				
			||||||
                                const SelectInterruptPtr& selectInterrupt)
 | 
					                                const SelectInterruptPtr& selectInterrupt,
 | 
				
			||||||
 | 
					                                int kqueuefd)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#if defined(__APPLE__)
 | 
				
			||||||
 | 
					        // FIXME int kqueuefd = kqueue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        struct kevent ke;
 | 
				
			||||||
 | 
					        EV_SET(&ke, sockfd, (readyToRead) ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL);
 | 
				
			||||||
 | 
					        if (kevent(kqueuefd, &ke, 1, NULL, 0, NULL) == -1) return PollResultType::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int retval, numevents = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int nfds = 1;
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					        if (selectInterrupt) 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            nfds = 2;
 | 
				
			||||||
 | 
					            int interruptFd = selectInterrupt->getFd();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            struct kevent ke;
 | 
				
			||||||
 | 
					            EV_SET(&ke, interruptFd, EVFILT_READ, EV_ADD, 0, 0, NULL);
 | 
				
			||||||
 | 
					            if (kevent(kqueuefd, &ke, 1, NULL, 0, NULL) == -1) return PollResultType::Error;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        struct kevent *events;
 | 
				
			||||||
 | 
					        events = (struct kevent*) malloc(sizeof(struct kevent) * nfds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (timeoutMs != 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            struct timespec timeout;
 | 
				
			||||||
 | 
					            timeout.tv_sec = timeoutMs / 1000;
 | 
				
			||||||
 | 
					            timeout.tv_nsec = (timeoutMs % 1000) * 1000 * 1000;
 | 
				
			||||||
 | 
					            retval = kevent(kqueuefd, NULL, 0, events, nfds, &timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            retval = kevent(kqueuefd, NULL, 0, events, nfds, NULL);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					        if (retval > 0) {
 | 
				
			||||||
 | 
					            int j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            numevents = retval;
 | 
				
			||||||
 | 
					            for(j = 0; j < numevents; j++) {
 | 
				
			||||||
 | 
					                int mask = 0;
 | 
				
			||||||
 | 
					                struct kevent *e = events+j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (e->filter == EVFILT_READ) mask |= AE_READABLE;
 | 
				
			||||||
 | 
					                if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE;
 | 
				
			||||||
 | 
					                eventLoop->fired[j].fd = e->ident;
 | 
				
			||||||
 | 
					                eventLoop->fired[j].mask = mask;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        PollResultType pollResult = PollResultType::ReadyForRead;
 | 
				
			||||||
 | 
					        if (retval < 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            pollResult = PollResultType::Error;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (retval > 0) {
 | 
				
			||||||
 | 
					            struct kevent *e = events;
 | 
				
			||||||
 | 
					            if (e->filter == EVFILT_READ)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                pollResult = PollResultType::ReadyForRead;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (e->filter == EVFILT_WRITE) 
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                pollResult = PollResultType::ReadyForWrite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                int optval = -1;
 | 
				
			||||||
 | 
					                socklen_t optlen = sizeof(optval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // getsockopt() puts the errno value for connect into optval so 0
 | 
				
			||||||
 | 
					                // means no-error.
 | 
				
			||||||
 | 
					                if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1 || optval != 0)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    pollResult = PollResultType::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // set errno to optval so that external callers can have an
 | 
				
			||||||
 | 
					                    // appropriate error description when calling strerror
 | 
				
			||||||
 | 
					                    errno = optval;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            pollResult = PollResultType::Timeout;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        free(events);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ::close(kqueuefd); //FMXE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return pollResult;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
        // We used to use ::select to poll but on Android 9 we get large fds out of
 | 
					        // We used to use ::select to poll but on Android 9 we get large fds out of
 | 
				
			||||||
        // ::connect which crash in FD_SET as they are larger than FD_SETSIZE. Switching
 | 
					        // ::connect which crash in FD_SET as they are larger than FD_SETSIZE. Switching
 | 
				
			||||||
@@ -142,6 +245,7 @@ namespace ix
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return pollResult;
 | 
					        return pollResult;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PollResultType Socket::isReadyToRead(int timeoutMs)
 | 
					    PollResultType Socket::isReadyToRead(int timeoutMs)
 | 
				
			||||||
@@ -152,7 +256,7 @@ namespace ix
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool readyToRead = true;
 | 
					        bool readyToRead = true;
 | 
				
			||||||
        return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt);
 | 
					        return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt, _kqueuefd);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PollResultType Socket::isReadyToWrite(int timeoutMs)
 | 
					    PollResultType Socket::isReadyToWrite(int timeoutMs)
 | 
				
			||||||
@@ -163,7 +267,7 @@ namespace ix
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool readyToRead = false;
 | 
					        bool readyToRead = false;
 | 
				
			||||||
        return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt);
 | 
					        return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt, _kqueuefd);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Wake up from poll/select by writing to the pipe which is watched by select
 | 
					    // Wake up from poll/select by writing to the pipe which is watched by select
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,13 @@
 | 
				
			|||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For kqueue
 | 
				
			||||||
 | 
					#if defined(__APPLE__)
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <sys/event.h>
 | 
				
			||||||
 | 
					#include <sys/time.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef _WIN32
 | 
					#ifdef _WIN32
 | 
				
			||||||
#include <BaseTsd.h>
 | 
					#include <BaseTsd.h>
 | 
				
			||||||
typedef SSIZE_T ssize_t;
 | 
					typedef SSIZE_T ssize_t;
 | 
				
			||||||
@@ -94,7 +101,8 @@ namespace ix
 | 
				
			|||||||
        static PollResultType poll(bool readyToRead,
 | 
					        static PollResultType poll(bool readyToRead,
 | 
				
			||||||
                                   int timeoutMs,
 | 
					                                   int timeoutMs,
 | 
				
			||||||
                                   int sockfd,
 | 
					                                   int sockfd,
 | 
				
			||||||
                                   const SelectInterruptPtr& selectInterrupt);
 | 
					                                   const SelectInterruptPtr& selectInterrupt,
 | 
				
			||||||
 | 
					                                   int kqueuefd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Used as special codes for pipe communication
 | 
					        // Used as special codes for pipe communication
 | 
				
			||||||
@@ -114,5 +122,9 @@ namespace ix
 | 
				
			|||||||
        static constexpr size_t kChunkSize = 1 << 15;
 | 
					        static constexpr size_t kChunkSize = 1 << 15;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SelectInterruptPtr _selectInterrupt;
 | 
					        SelectInterruptPtr _selectInterrupt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(__APPLE__)
 | 
				
			||||||
 | 
					        int _kqueuefd;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,7 +66,10 @@ namespace ix
 | 
				
			|||||||
            int timeoutMs = 10;
 | 
					            int timeoutMs = 10;
 | 
				
			||||||
            bool readyToRead = false;
 | 
					            bool readyToRead = false;
 | 
				
			||||||
            auto selectInterrupt = std::make_unique<SelectInterrupt>();
 | 
					            auto selectInterrupt = std::make_unique<SelectInterrupt>();
 | 
				
			||||||
            PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
 | 
					
 | 
				
			||||||
 | 
					            int kqueuefd = kqueue();
 | 
				
			||||||
 | 
					            PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt, kqueuefd);
 | 
				
			||||||
 | 
					            ::close(kqueuefd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (pollResult == PollResultType::Timeout)
 | 
					            if (pollResult == PollResultType::Timeout)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -259,8 +259,11 @@ namespace ix
 | 
				
			|||||||
            int timeoutMs = 10;
 | 
					            int timeoutMs = 10;
 | 
				
			||||||
            bool readyToRead = true;
 | 
					            bool readyToRead = true;
 | 
				
			||||||
            auto selectInterrupt = std::make_unique<SelectInterrupt>();
 | 
					            auto selectInterrupt = std::make_unique<SelectInterrupt>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int kqueuefd = kqueue();
 | 
				
			||||||
            PollResultType pollResult =
 | 
					            PollResultType pollResult =
 | 
				
			||||||
                Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt);
 | 
					                Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt, kqueuefd);
 | 
				
			||||||
 | 
					            ::close(kqueuefd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (pollResult == PollResultType::Error)
 | 
					            if (pollResult == PollResultType::Error)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "IXWebSocketVersion.h"
 | 
					#include "IXWebSocketVersion.h"
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
#include <zlib.h>
 | 
					#include <zlib.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Platform name
 | 
					// Platform name
 | 
				
			||||||
#if defined(_WIN32)
 | 
					#if defined(_WIN32)
 | 
				
			||||||
@@ -77,8 +79,10 @@ namespace ix
 | 
				
			|||||||
        ss << " nossl";
 | 
					        ss << " nossl";
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        // Zlib version
 | 
					        // Zlib version
 | 
				
			||||||
        ss << " zlib " << ZLIB_VERSION;
 | 
					        ss << " zlib " << ZLIB_VERSION;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ss.str();
 | 
					        return ss.str();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,21 +28,26 @@ namespace ix
 | 
				
			|||||||
    WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
 | 
					    WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
 | 
				
			||||||
        : _compressBufferSize(kBufferSize)
 | 
					        : _compressBufferSize(kBufferSize)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        memset(&_deflateState, 0, sizeof(_deflateState));
 | 
					        memset(&_deflateState, 0, sizeof(_deflateState));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _deflateState.zalloc = Z_NULL;
 | 
					        _deflateState.zalloc = Z_NULL;
 | 
				
			||||||
        _deflateState.zfree = Z_NULL;
 | 
					        _deflateState.zfree = Z_NULL;
 | 
				
			||||||
        _deflateState.opaque = Z_NULL;
 | 
					        _deflateState.opaque = Z_NULL;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor()
 | 
					    WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        deflateEnd(&_deflateState);
 | 
					        deflateEnd(&_deflateState);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
 | 
					    bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
 | 
				
			||||||
                                                    bool clientNoContextTakeOver)
 | 
					                                                    bool clientNoContextTakeOver)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        int ret = deflateInit2(&_deflateState,
 | 
					        int ret = deflateInit2(&_deflateState,
 | 
				
			||||||
                               Z_DEFAULT_COMPRESSION,
 | 
					                               Z_DEFAULT_COMPRESSION,
 | 
				
			||||||
                               Z_DEFLATED,
 | 
					                               Z_DEFLATED,
 | 
				
			||||||
@@ -57,6 +62,9 @@ namespace ix
 | 
				
			|||||||
        _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
 | 
					        _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template<typename T>
 | 
					    template<typename T>
 | 
				
			||||||
@@ -96,6 +104,7 @@ namespace ix
 | 
				
			|||||||
    template<typename T, typename S>
 | 
					    template<typename T, typename S>
 | 
				
			||||||
    bool WebSocketPerMessageDeflateCompressor::compressData(const T& in, S& out)
 | 
					    bool WebSocketPerMessageDeflateCompressor::compressData(const T& in, S& out)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
        // 7.2.1.  Compression
 | 
					        // 7.2.1.  Compression
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
@@ -152,6 +161,9 @@ namespace ix
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
@@ -160,6 +172,7 @@ namespace ix
 | 
				
			|||||||
    WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
 | 
					    WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
 | 
				
			||||||
        : _compressBufferSize(kBufferSize)
 | 
					        : _compressBufferSize(kBufferSize)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        memset(&_inflateState, 0, sizeof(_inflateState));
 | 
					        memset(&_inflateState, 0, sizeof(_inflateState));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _inflateState.zalloc = Z_NULL;
 | 
					        _inflateState.zalloc = Z_NULL;
 | 
				
			||||||
@@ -167,16 +180,20 @@ namespace ix
 | 
				
			|||||||
        _inflateState.opaque = Z_NULL;
 | 
					        _inflateState.opaque = Z_NULL;
 | 
				
			||||||
        _inflateState.avail_in = 0;
 | 
					        _inflateState.avail_in = 0;
 | 
				
			||||||
        _inflateState.next_in = Z_NULL;
 | 
					        _inflateState.next_in = Z_NULL;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
 | 
					    WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        inflateEnd(&_inflateState);
 | 
					        inflateEnd(&_inflateState);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
 | 
					    bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
 | 
				
			||||||
                                                      bool clientNoContextTakeOver)
 | 
					                                                      bool clientNoContextTakeOver)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        int ret = inflateInit2(&_inflateState, -1 * inflateBits);
 | 
					        int ret = inflateInit2(&_inflateState, -1 * inflateBits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (ret != Z_OK) return false;
 | 
					        if (ret != Z_OK) return false;
 | 
				
			||||||
@@ -186,10 +203,14 @@ namespace ix
 | 
				
			|||||||
        _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
 | 
					        _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
 | 
					    bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
        // 7.2.2.  Decompression
 | 
					        // 7.2.2.  Decompression
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
@@ -226,5 +247,8 @@ namespace ix
 | 
				
			|||||||
        } while (_inflateState.avail_out == 0);
 | 
					        } while (_inflateState.avail_out == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
#include "zlib.h"
 | 
					#include "zlib.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
@@ -34,7 +36,10 @@ namespace ix
 | 
				
			|||||||
        int _flush;
 | 
					        int _flush;
 | 
				
			||||||
        size_t _compressBufferSize;
 | 
					        size_t _compressBufferSize;
 | 
				
			||||||
        std::unique_ptr<unsigned char[]> _compressBuffer;
 | 
					        std::unique_ptr<unsigned char[]> _compressBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        z_stream _deflateState;
 | 
					        z_stream _deflateState;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class WebSocketPerMessageDeflateDecompressor
 | 
					    class WebSocketPerMessageDeflateDecompressor
 | 
				
			||||||
@@ -50,7 +55,10 @@ namespace ix
 | 
				
			|||||||
        int _flush;
 | 
					        int _flush;
 | 
				
			||||||
        size_t _compressBufferSize;
 | 
					        size_t _compressBufferSize;
 | 
				
			||||||
        std::unique_ptr<unsigned char[]> _compressBuffer;
 | 
					        std::unique_ptr<unsigned char[]> _compressBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        z_stream _inflateState;
 | 
					        z_stream _inflateState;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,6 +61,7 @@ namespace ix
 | 
				
			|||||||
        _clientMaxWindowBits = kDefaultClientMaxWindowBits;
 | 
					        _clientMaxWindowBits = kDefaultClientMaxWindowBits;
 | 
				
			||||||
        _serverMaxWindowBits = kDefaultServerMaxWindowBits;
 | 
					        _serverMaxWindowBits = kDefaultServerMaxWindowBits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        // Split by ;
 | 
					        // Split by ;
 | 
				
			||||||
        std::string token;
 | 
					        std::string token;
 | 
				
			||||||
        std::stringstream tokenStream(extension);
 | 
					        std::stringstream tokenStream(extension);
 | 
				
			||||||
@@ -112,6 +113,7 @@ namespace ix
 | 
				
			|||||||
                sanitizeClientMaxWindowBits();
 | 
					                sanitizeClientMaxWindowBits();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits()
 | 
					    void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits()
 | 
				
			||||||
@@ -126,6 +128,7 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    std::string WebSocketPerMessageDeflateOptions::generateHeader()
 | 
					    std::string WebSocketPerMessageDeflateOptions::generateHeader()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        std::stringstream ss;
 | 
					        std::stringstream ss;
 | 
				
			||||||
        ss << "Sec-WebSocket-Extensions: permessage-deflate";
 | 
					        ss << "Sec-WebSocket-Extensions: permessage-deflate";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,11 +141,18 @@ namespace ix
 | 
				
			|||||||
        ss << "\r\n";
 | 
					        ss << "\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ss.str();
 | 
					        return ss.str();
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        return std::string();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketPerMessageDeflateOptions::enabled() const
 | 
					    bool WebSocketPerMessageDeflateOptions::enabled() const
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_ZLIB
 | 
				
			||||||
        return _enabled;
 | 
					        return _enabled;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const
 | 
					    bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,4 +6,4 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define IX_WEBSOCKET_VERSION "10.1.1"
 | 
					#define IX_WEBSOCKET_VERSION "10.1.5"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								makefile
									
									
									
									
									
								
							@@ -34,7 +34,10 @@ ws:
 | 
				
			|||||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
 | 
						mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ws_install:
 | 
					ws_install:
 | 
				
			||||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && make -j 4 install)
 | 
						mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ws_install_release:
 | 
				
			||||||
 | 
						mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ws_openssl:
 | 
					ws_openssl:
 | 
				
			||||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
 | 
						mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										189
									
								
								test/compatibility/cpp/libwebsockets/devnull_client.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								test/compatibility/cpp/libwebsockets/devnull_client.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lws-minimal-ws-client
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Written in 2010-2019 by Andy Green <andy@warmcat.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is made available under the Creative Commons CC0 1.0
 | 
				
			||||||
 | 
					 * Universal Public Domain Dedication.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This demonstrates the a minimal ws client using lws.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Original programs connects to https://libwebsockets.org/ and makes a
 | 
				
			||||||
 | 
					 * wss connection to the dumb-increment protocol there.  While
 | 
				
			||||||
 | 
					 * connected, it prints the numbers it is being sent by
 | 
				
			||||||
 | 
					 * dumb-increment protocol.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is modified to make a test client which counts how much messages
 | 
				
			||||||
 | 
					 * per second can be received.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * libwebsockets$ make && ./a.out
 | 
				
			||||||
 | 
					 * g++ --std=c++14 -I/usr/local/opt/openssl/include devnull_client.cpp -lwebsockets
 | 
				
			||||||
 | 
					 * messages received: 0 per second 0 total
 | 
				
			||||||
 | 
					 * [2020/08/02 19:22:21:4774] U: LWS minimal ws client rx [-d <logs>] [--h2]
 | 
				
			||||||
 | 
					 * [2020/08/02 19:22:21:4814] U: callback_dumb_increment: established
 | 
				
			||||||
 | 
					 * messages received: 0 per second 0 total
 | 
				
			||||||
 | 
					 * messages received: 180015 per second 180015 total
 | 
				
			||||||
 | 
					 * messages received: 172866 per second 352881 total
 | 
				
			||||||
 | 
					 * messages received: 176177 per second 529058 total
 | 
				
			||||||
 | 
					 * messages received: 174191 per second 703249 total
 | 
				
			||||||
 | 
					 * messages received: 193397 per second 896646 total
 | 
				
			||||||
 | 
					 * messages received: 196385 per second 1093031 total
 | 
				
			||||||
 | 
					 * messages received: 194593 per second 1287624 total
 | 
				
			||||||
 | 
					 * messages received: 189484 per second 1477108 total
 | 
				
			||||||
 | 
					 * messages received: 200825 per second 1677933 total
 | 
				
			||||||
 | 
					 * messages received: 183542 per second 1861475 total
 | 
				
			||||||
 | 
					 * ^C[2020/08/02 19:22:33:4450] U: Completed OK
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libwebsockets.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int interrupted;
 | 
				
			||||||
 | 
					static struct lws *client_wsi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::atomic<uint64_t> receivedCount(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
 | 
				
			||||||
 | 
					              void *user, void *in, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					        switch (reason) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* because we are protocols[0] ... */
 | 
				
			||||||
 | 
					        case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
 | 
				
			||||||
 | 
					                lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
 | 
				
			||||||
 | 
					                         in ? (char *)in : "(null)");
 | 
				
			||||||
 | 
					                client_wsi = NULL;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case LWS_CALLBACK_CLIENT_ESTABLISHED:
 | 
				
			||||||
 | 
					                lwsl_user("%s: established\n", __func__);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case LWS_CALLBACK_CLIENT_RECEIVE:
 | 
				
			||||||
 | 
					                receivedCount++;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case LWS_CALLBACK_CLIENT_CLOSED:
 | 
				
			||||||
 | 
					                client_wsi = NULL;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return lws_callback_http_dummy(wsi, reason, user, in, len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct lws_protocols protocols[] = {
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                "dumb-increment-protocol",
 | 
				
			||||||
 | 
					                callback_dumb_increment,
 | 
				
			||||||
 | 
					                0,
 | 
				
			||||||
 | 
					                0,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        { NULL, NULL, 0, 0 }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					sigint_handler(int sig)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					        interrupted = 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, const char **argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					        uint64_t receivedCountTotal(0);
 | 
				
			||||||
 | 
					        uint64_t receivedCountPerSecs(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto timer = [&receivedCountTotal, &receivedCountPerSecs] {
 | 
				
			||||||
 | 
					            while (!interrupted)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::cerr << "messages received: "
 | 
				
			||||||
 | 
					                          << receivedCountPerSecs
 | 
				
			||||||
 | 
					                          << " per second "
 | 
				
			||||||
 | 
					                          << receivedCountTotal 
 | 
				
			||||||
 | 
					                          << " total"
 | 
				
			||||||
 | 
					                          << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                receivedCountPerSecs = receivedCount - receivedCountTotal;
 | 
				
			||||||
 | 
					                receivedCountTotal += receivedCountPerSecs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                auto duration = std::chrono::seconds(1);
 | 
				
			||||||
 | 
					                std::this_thread::sleep_for(duration);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::thread t1(timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        struct lws_context_creation_info info;
 | 
				
			||||||
 | 
					        struct lws_client_connect_info i;
 | 
				
			||||||
 | 
					        struct lws_context *context;
 | 
				
			||||||
 | 
					        const char *p;
 | 
				
			||||||
 | 
					        int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
 | 
				
			||||||
 | 
					                /* for LLL_ verbosity above NOTICE to be built into lws, lws
 | 
				
			||||||
 | 
					                 * must have been configured with -DCMAKE_BUILD_TYPE=DEBUG
 | 
				
			||||||
 | 
					                 * instead of =RELEASE */
 | 
				
			||||||
 | 
					                /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
 | 
				
			||||||
 | 
					                /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
 | 
				
			||||||
 | 
					                /* | LLL_DEBUG */;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        signal(SIGINT, sigint_handler);
 | 
				
			||||||
 | 
					        if ((p = lws_cmdline_option(argc, argv, "-d")))
 | 
				
			||||||
 | 
					                logs = atoi(p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lws_set_log_level(logs, NULL);
 | 
				
			||||||
 | 
					        lwsl_user("LWS minimal ws client rx [-d <logs>] [--h2]\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
 | 
				
			||||||
 | 
					        info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
 | 
				
			||||||
 | 
					        info.protocols = protocols;
 | 
				
			||||||
 | 
					        info.timeout_secs = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					         * since we know this lws context is only ever going to be used with
 | 
				
			||||||
 | 
					         * one client wsis / fds / sockets at a time, let lws know it doesn't
 | 
				
			||||||
 | 
					         * have to use the default allocations for fd tables up to ulimit -n.
 | 
				
			||||||
 | 
					         * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
 | 
				
			||||||
 | 
					         * will use.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        info.fd_limit_per_thread = 1 + 1 + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context = lws_create_context(&info);
 | 
				
			||||||
 | 
					        if (!context) {
 | 
				
			||||||
 | 
					                lwsl_err("lws init failed\n");
 | 
				
			||||||
 | 
					                return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
 | 
				
			||||||
 | 
					        i.context = context;
 | 
				
			||||||
 | 
					        i.port = 8008;
 | 
				
			||||||
 | 
					        i.address = "127.0.0.1";
 | 
				
			||||||
 | 
					        i.path = "/";
 | 
				
			||||||
 | 
					        i.host = i.address;
 | 
				
			||||||
 | 
					        i.origin = i.address;
 | 
				
			||||||
 | 
					        i.protocol = protocols[0].name; /* "dumb-increment-protocol" */
 | 
				
			||||||
 | 
					        i.pwsi = &client_wsi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (lws_cmdline_option(argc, argv, "--h2"))
 | 
				
			||||||
 | 
					                i.alpn = "h2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lws_client_connect_via_info(&i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (n >= 0 && client_wsi && !interrupted)
 | 
				
			||||||
 | 
					                n = lws_service(context, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lws_context_destroy(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lwsl_user("Completed %s\n", receivedCount > 10 ? "OK" : "Failed");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        t1.join();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return receivedCount > 10;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								test/compatibility/csharp/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								test/compatibility/csharp/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					bin
 | 
				
			||||||
 | 
					obj
 | 
				
			||||||
							
								
								
									
										99
									
								
								test/compatibility/csharp/Main.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								test/compatibility/csharp/Main.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Main.cs
 | 
				
			||||||
 | 
					// Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					// Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In a different terminal, start a push server:
 | 
				
			||||||
 | 
					// $ ws push_server -q
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// $ dotnet run
 | 
				
			||||||
 | 
					// messages received per second: 145157
 | 
				
			||||||
 | 
					// messages received per second: 141405
 | 
				
			||||||
 | 
					// messages received per second: 152202
 | 
				
			||||||
 | 
					// messages received per second: 157149
 | 
				
			||||||
 | 
					// messages received per second: 157673
 | 
				
			||||||
 | 
					// messages received per second: 153594
 | 
				
			||||||
 | 
					// messages received per second: 157830
 | 
				
			||||||
 | 
					// messages received per second: 158422
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Net.WebSockets;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DevNullClientCli
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private static int receivedMessage = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static async Task<byte[]> ReceiveAsync(ClientWebSocket ws, CancellationToken token)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        int bufferSize = 8192; // 8K
 | 
				
			||||||
 | 
					        var buffer = new byte[bufferSize];
 | 
				
			||||||
 | 
					        var offset = 0;
 | 
				
			||||||
 | 
					        var free = buffer.Length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (true)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer, offset, free), token).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            offset += result.Count;
 | 
				
			||||||
 | 
					            free -= result.Count;
 | 
				
			||||||
 | 
					            if (result.EndOfMessage) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (free == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // No free space
 | 
				
			||||||
 | 
					                // Resize the outgoing buffer
 | 
				
			||||||
 | 
					                var newSize = buffer.Length + bufferSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var newBuffer = new byte[newSize];
 | 
				
			||||||
 | 
					                Array.Copy(buffer, 0, newBuffer, 0, offset);
 | 
				
			||||||
 | 
					                buffer = newBuffer;
 | 
				
			||||||
 | 
					                free = buffer.Length - offset;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return buffer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static void OnTimedEvent(object source, EventArgs e)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Console.WriteLine($"messages received per second: {receivedMessage}");
 | 
				
			||||||
 | 
					        receivedMessage = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static async Task ReceiveMessagesAsync(string url)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var ws = new ClientWebSocket();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        System.Uri uri = new System.Uri(url);
 | 
				
			||||||
 | 
					        var cancellationToken = CancellationToken.None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await ws.ConnectAsync(uri, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					            while (true)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var data = await DevNullClientCli.ReceiveAsync(ws, cancellationToken);
 | 
				
			||||||
 | 
					                receivedMessage += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (System.Net.WebSockets.WebSocketException e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Console.WriteLine($"WebSocket error: {e}");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static async Task Main()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var timer = new System.Timers.Timer(1000);
 | 
				
			||||||
 | 
					        timer.Elapsed += OnTimedEvent;
 | 
				
			||||||
 | 
					        timer.Enabled = true;
 | 
				
			||||||
 | 
					        timer.Start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var url = "ws://localhost:8008";
 | 
				
			||||||
 | 
					        await ReceiveMessagesAsync(url);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								test/compatibility/csharp/devnull_client.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								test/compatibility/csharp/devnull_client.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					  <PropertyGroup>
 | 
				
			||||||
 | 
					    <OutputType>Exe</OutputType>
 | 
				
			||||||
 | 
					    <TargetFramework>netcoreapp3.1</TargetFramework>
 | 
				
			||||||
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										42
									
								
								test/compatibility/node/devnull_client.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								test/compatibility/node/devnull_client.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// With ws@7.3.1
 | 
				
			||||||
 | 
					// and 
 | 
				
			||||||
 | 
					// node --version
 | 
				
			||||||
 | 
					// v13.11.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In a different terminal, start a push server:
 | 
				
			||||||
 | 
					// $ ws push_server -q
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// $ node devnull_client.js
 | 
				
			||||||
 | 
					// messages received per second: 16643
 | 
				
			||||||
 | 
					// messages received per second: 28065
 | 
				
			||||||
 | 
					// messages received per second: 28432
 | 
				
			||||||
 | 
					// messages received per second: 22207
 | 
				
			||||||
 | 
					// messages received per second: 28805
 | 
				
			||||||
 | 
					// messages received per second: 28694
 | 
				
			||||||
 | 
					// messages received per second: 28180
 | 
				
			||||||
 | 
					// messages received per second: 28601
 | 
				
			||||||
 | 
					// messages received per second: 28698
 | 
				
			||||||
 | 
					// messages received per second: 28931
 | 
				
			||||||
 | 
					// messages received per second: 27975
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					const WebSocket = require('ws');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ws = new WebSocket('ws://localhost:8008');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ws.on('open', function open() {
 | 
				
			||||||
 | 
					  ws.send('hello from node');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var receivedMessages = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setInterval(function timeout() {
 | 
				
			||||||
 | 
					  console.log(`messages received per second: ${receivedMessages}`)
 | 
				
			||||||
 | 
					  receivedMessages = 0;
 | 
				
			||||||
 | 
					}, 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ws.on('message', function incoming(data) {
 | 
				
			||||||
 | 
					  receivedMessages += 1;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										44
									
								
								test/compatibility/python/websockets/devnull_client.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								test/compatibility/python/websockets/devnull_client.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# websocket send client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import argparse
 | 
				
			||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					import websockets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    import uvloop
 | 
				
			||||||
 | 
					    uvloop.install()
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
					    print('uvloop not available')
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgCount = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def timer():
 | 
				
			||||||
 | 
					    global msgCount
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        print(f'Received messages: {msgCount}')
 | 
				
			||||||
 | 
					        msgCount = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await asyncio.sleep(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def client(url):
 | 
				
			||||||
 | 
					    global msgCount
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    asyncio.ensure_future(timer())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async with websockets.connect(url) as ws:
 | 
				
			||||||
 | 
					        async for message in ws:
 | 
				
			||||||
 | 
					            msgCount += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    parser = argparse.ArgumentParser(description='websocket proxy.')
 | 
				
			||||||
 | 
					    parser.add_argument('--url', help='Remote websocket url',
 | 
				
			||||||
 | 
					                        default='wss://echo.websocket.org')
 | 
				
			||||||
 | 
					    args = parser.parse_args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    asyncio.get_event_loop().run_until_complete(client(args.url))
 | 
				
			||||||
@@ -10,7 +10,7 @@ import websockets
 | 
				
			|||||||
async def echo(websocket, path):
 | 
					async def echo(websocket, path):
 | 
				
			||||||
    while True:
 | 
					    while True:
 | 
				
			||||||
        msg = await websocket.recv()
 | 
					        msg = await websocket.recv()
 | 
				
			||||||
        print(f'Received {len(msg)} bytes')
 | 
					        # print(f'Received {len(msg)} bytes')
 | 
				
			||||||
        await websocket.send(msg)
 | 
					        await websocket.send(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
host = os.getenv('BIND_HOST', 'localhost')
 | 
					host = os.getenv('BIND_HOST', 'localhost')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,9 @@ add_executable(ws
 | 
				
			|||||||
  ws_http_client.cpp
 | 
					  ws_http_client.cpp
 | 
				
			||||||
  ws_ping_pong.cpp
 | 
					  ws_ping_pong.cpp
 | 
				
			||||||
  ws_broadcast_server.cpp
 | 
					  ws_broadcast_server.cpp
 | 
				
			||||||
 | 
					  ws_push_server.cpp
 | 
				
			||||||
  ws_echo_server.cpp
 | 
					  ws_echo_server.cpp
 | 
				
			||||||
 | 
					  ws_echo_client.cpp
 | 
				
			||||||
  ws_chat.cpp
 | 
					  ws_chat.cpp
 | 
				
			||||||
  ws_connect.cpp
 | 
					  ws_connect.cpp
 | 
				
			||||||
  ws_transfer.cpp
 | 
					  ws_transfer.cpp
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								ws/ws.cpp
									
									
									
									
									
								
							@@ -125,6 +125,7 @@ int main(int argc, char** argv)
 | 
				
			|||||||
    std::string logfile;
 | 
					    std::string logfile;
 | 
				
			||||||
    std::string scriptPath;
 | 
					    std::string scriptPath;
 | 
				
			||||||
    std::string republishChannel;
 | 
					    std::string republishChannel;
 | 
				
			||||||
 | 
					    std::string sendMsg("hello world");
 | 
				
			||||||
    ix::SocketTLSOptions tlsOptions;
 | 
					    ix::SocketTLSOptions tlsOptions;
 | 
				
			||||||
    ix::CobraConfig cobraConfig;
 | 
					    ix::CobraConfig cobraConfig;
 | 
				
			||||||
    ix::CobraBotConfig cobraBotConfig;
 | 
					    ix::CobraBotConfig cobraBotConfig;
 | 
				
			||||||
@@ -147,6 +148,7 @@ int main(int argc, char** argv)
 | 
				
			|||||||
    bool version = false;
 | 
					    bool version = false;
 | 
				
			||||||
    bool verifyNone = false;
 | 
					    bool verifyNone = false;
 | 
				
			||||||
    bool disablePong = false;
 | 
					    bool disablePong = false;
 | 
				
			||||||
 | 
					    bool noSend = false;
 | 
				
			||||||
    int port = 8008;
 | 
					    int port = 8008;
 | 
				
			||||||
    int redisPort = 6379;
 | 
					    int redisPort = 6379;
 | 
				
			||||||
    int statsdPort = 8125;
 | 
					    int statsdPort = 8125;
 | 
				
			||||||
@@ -243,6 +245,19 @@ int main(int argc, char** argv)
 | 
				
			|||||||
    connectApp->add_option("--subprotocol", subprotocol, "Subprotocol");
 | 
					    connectApp->add_option("--subprotocol", subprotocol, "Subprotocol");
 | 
				
			||||||
    addTLSOptions(connectApp);
 | 
					    addTLSOptions(connectApp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* echoClientApp =
 | 
				
			||||||
 | 
					        app.add_subcommand("echo_client", "Echo messages sent by a remote server");
 | 
				
			||||||
 | 
					    echoClientApp->fallthrough();
 | 
				
			||||||
 | 
					    echoClientApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
 | 
					    echoClientApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
 | 
				
			||||||
 | 
					    echoClientApp->add_flag("-b", binaryMode, "Send in binary mode");
 | 
				
			||||||
 | 
					    echoClientApp->add_option(
 | 
				
			||||||
 | 
					        "--ping_interval", pingIntervalSecs, "Interval between sending pings");
 | 
				
			||||||
 | 
					    echoClientApp->add_option("--subprotocol", subprotocol, "Subprotocol");
 | 
				
			||||||
 | 
					    echoClientApp->add_option("--send_msg", sendMsg, "Send message");
 | 
				
			||||||
 | 
					    echoClientApp->add_flag("-m", noSend, "Do not send messages, only receive messages");
 | 
				
			||||||
 | 
					    addTLSOptions(echoClientApp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
 | 
					    CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
 | 
				
			||||||
    chatApp->fallthrough();
 | 
					    chatApp->fallthrough();
 | 
				
			||||||
    chatApp->add_option("url", url, "Connection url")->required();
 | 
					    chatApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
@@ -252,12 +267,25 @@ int main(int argc, char** argv)
 | 
				
			|||||||
    echoServerApp->fallthrough();
 | 
					    echoServerApp->fallthrough();
 | 
				
			||||||
    echoServerApp->add_option("--port", port, "Port");
 | 
					    echoServerApp->add_option("--port", port, "Port");
 | 
				
			||||||
    echoServerApp->add_option("--host", hostname, "Hostname");
 | 
					    echoServerApp->add_option("--host", hostname, "Hostname");
 | 
				
			||||||
    echoServerApp->add_flag("-g", greetings, "Verbose");
 | 
					    echoServerApp->add_flag("-q", quiet, "Quiet / only display warnings and errors");
 | 
				
			||||||
 | 
					    echoServerApp->add_flag("-g", greetings, "Greet");
 | 
				
			||||||
    echoServerApp->add_flag("-6", ipv6, "IpV6");
 | 
					    echoServerApp->add_flag("-6", ipv6, "IpV6");
 | 
				
			||||||
    echoServerApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
 | 
					    echoServerApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
 | 
				
			||||||
    echoServerApp->add_flag("-p", disablePong, "Disable sending PONG in response to PING");
 | 
					    echoServerApp->add_flag("-p", disablePong, "Disable sending PONG in response to PING");
 | 
				
			||||||
    addTLSOptions(echoServerApp);
 | 
					    addTLSOptions(echoServerApp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* pushServerApp = app.add_subcommand("push_server", "Push server");
 | 
				
			||||||
 | 
					    pushServerApp->fallthrough();
 | 
				
			||||||
 | 
					    pushServerApp->add_option("--port", port, "Port");
 | 
				
			||||||
 | 
					    pushServerApp->add_option("--host", hostname, "Hostname");
 | 
				
			||||||
 | 
					    pushServerApp->add_flag("-q", quiet, "Quiet / only display warnings and errors");
 | 
				
			||||||
 | 
					    pushServerApp->add_flag("-g", greetings, "Greet");
 | 
				
			||||||
 | 
					    pushServerApp->add_flag("-6", ipv6, "IpV6");
 | 
				
			||||||
 | 
					    pushServerApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
 | 
				
			||||||
 | 
					    pushServerApp->add_flag("-p", disablePong, "Disable sending PONG in response to PING");
 | 
				
			||||||
 | 
					    pushServerApp->add_option("--send_msg", sendMsg, "Send message");
 | 
				
			||||||
 | 
					    addTLSOptions(pushServerApp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
 | 
					    CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
 | 
				
			||||||
    broadcastServerApp->fallthrough();
 | 
					    broadcastServerApp->fallthrough();
 | 
				
			||||||
    broadcastServerApp->add_option("--port", port, "Port");
 | 
					    broadcastServerApp->add_option("--port", port, "Port");
 | 
				
			||||||
@@ -477,6 +505,11 @@ int main(int argc, char** argv)
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (quiet)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        spdlog::set_level(spdlog::level::warn);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Cobra config
 | 
					    // Cobra config
 | 
				
			||||||
    cobraConfig.webSocketPerMessageDeflateOptions = ix::WebSocketPerMessageDeflateOptions(true);
 | 
					    cobraConfig.webSocketPerMessageDeflateOptions = ix::WebSocketPerMessageDeflateOptions(true);
 | 
				
			||||||
    cobraConfig.socketTLSOptions = tlsOptions;
 | 
					    cobraConfig.socketTLSOptions = tlsOptions;
 | 
				
			||||||
@@ -498,11 +531,33 @@ int main(int argc, char** argv)
 | 
				
			|||||||
                                  subprotocol,
 | 
					                                  subprotocol,
 | 
				
			||||||
                                  pingIntervalSecs);
 | 
					                                  pingIntervalSecs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("echo_client"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret = ix::ws_echo_client(url,
 | 
				
			||||||
 | 
					                                 disablePerMessageDeflate,
 | 
				
			||||||
 | 
					                                 binaryMode,
 | 
				
			||||||
 | 
					                                 tlsOptions,
 | 
				
			||||||
 | 
					                                 subprotocol,
 | 
				
			||||||
 | 
					                                 pingIntervalSecs,
 | 
				
			||||||
 | 
					                                 sendMsg,
 | 
				
			||||||
 | 
					                                 noSend);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    else if (app.got_subcommand("echo_server"))
 | 
					    else if (app.got_subcommand("echo_server"))
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ret = ix::ws_echo_server_main(
 | 
					        ret = ix::ws_echo_server_main(
 | 
				
			||||||
            port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong);
 | 
					            port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("push_server"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ret = ix::ws_push_server(port,
 | 
				
			||||||
 | 
					                                 greetings,
 | 
				
			||||||
 | 
					                                 hostname,
 | 
				
			||||||
 | 
					                                 tlsOptions,
 | 
				
			||||||
 | 
					                                 ipv6,
 | 
				
			||||||
 | 
					                                 disablePerMessageDeflate,
 | 
				
			||||||
 | 
					                                 disablePong,
 | 
				
			||||||
 | 
					                                 sendMsg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    else if (app.got_subcommand("transfer"))
 | 
					    else if (app.got_subcommand("transfer"))
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ret = ix::ws_transfer_main(port, hostname, tlsOptions);
 | 
					        ret = ix::ws_transfer_main(port, hostname, tlsOptions);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								ws/ws.h
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								ws/ws.h
									
									
									
									
									
								
							@@ -35,6 +35,15 @@ namespace ix
 | 
				
			|||||||
                            bool disablePerMessageDeflate,
 | 
					                            bool disablePerMessageDeflate,
 | 
				
			||||||
                            bool disablePong);
 | 
					                            bool disablePong);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_push_server(int port,
 | 
				
			||||||
 | 
					                       bool greetings,
 | 
				
			||||||
 | 
					                       const std::string& hostname,
 | 
				
			||||||
 | 
					                       const ix::SocketTLSOptions& tlsOptions,
 | 
				
			||||||
 | 
					                       bool ipv6,
 | 
				
			||||||
 | 
					                       bool disablePerMessageDeflate,
 | 
				
			||||||
 | 
					                       bool disablePong,
 | 
				
			||||||
 | 
					                       const std::string& sendMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int ws_broadcast_server_main(int port,
 | 
					    int ws_broadcast_server_main(int port,
 | 
				
			||||||
                                 const std::string& hostname,
 | 
					                                 const std::string& hostname,
 | 
				
			||||||
                                 const ix::SocketTLSOptions& tlsOptions);
 | 
					                                 const ix::SocketTLSOptions& tlsOptions);
 | 
				
			||||||
@@ -54,6 +63,15 @@ namespace ix
 | 
				
			|||||||
                        const std::string& subprotocol,
 | 
					                        const std::string& subprotocol,
 | 
				
			||||||
                        int pingIntervalSecs);
 | 
					                        int pingIntervalSecs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_echo_client(const std::string& url,
 | 
				
			||||||
 | 
					                       bool disablePerMessageDeflate,
 | 
				
			||||||
 | 
					                       bool binaryMode,
 | 
				
			||||||
 | 
					                       const ix::SocketTLSOptions& tlsOptions,
 | 
				
			||||||
 | 
					                       const std::string& subprotocol,
 | 
				
			||||||
 | 
					                       int pingIntervalSecs,
 | 
				
			||||||
 | 
					                       const std::string& sendMsg,
 | 
				
			||||||
 | 
					                       bool noSend);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int ws_receive_main(const std::string& url,
 | 
					    int ws_receive_main(const std::string& url,
 | 
				
			||||||
                        bool enablePerMessageDeflate,
 | 
					                        bool enablePerMessageDeflate,
 | 
				
			||||||
                        int delayMs,
 | 
					                        int delayMs,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -160,7 +160,7 @@ namespace ix
 | 
				
			|||||||
            std::stringstream ss;
 | 
					            std::stringstream ss;
 | 
				
			||||||
            if (msg->type == ix::WebSocketMessageType::Open)
 | 
					            if (msg->type == ix::WebSocketMessageType::Open)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                log("ws_connect: connected");
 | 
					                spdlog::info("ws_connect: connected");
 | 
				
			||||||
                spdlog::info("Uri: {}", msg->openInfo.uri);
 | 
					                spdlog::info("Uri: {}", msg->openInfo.uri);
 | 
				
			||||||
                spdlog::info("Headers:");
 | 
					                spdlog::info("Headers:");
 | 
				
			||||||
                for (auto it : msg->openInfo.headers)
 | 
					                for (auto it : msg->openInfo.headers)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										121
									
								
								ws/ws_echo_client.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								ws/ws_echo_client.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws_echo_client.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <ixcore/utils/IXCoreLogger.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXNetSystem.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXSetThreadName.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocket.h>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ws_echo_client(const std::string& url,
 | 
				
			||||||
 | 
					                       bool disablePerMessageDeflate,
 | 
				
			||||||
 | 
					                       bool binaryMode,
 | 
				
			||||||
 | 
					                       const ix::SocketTLSOptions& tlsOptions,
 | 
				
			||||||
 | 
					                       const std::string& subprotocol,
 | 
				
			||||||
 | 
					                       int pingIntervalSecs,
 | 
				
			||||||
 | 
					                       const std::string& sendMsg,
 | 
				
			||||||
 | 
					                       bool noSend)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Our websocket object
 | 
				
			||||||
 | 
					        ix::WebSocket webSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        webSocket.setUrl(url);
 | 
				
			||||||
 | 
					        webSocket.setTLSOptions(tlsOptions);
 | 
				
			||||||
 | 
					        webSocket.setPingInterval(pingIntervalSecs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (disablePerMessageDeflate)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            webSocket.disablePerMessageDeflate();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!subprotocol.empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            webSocket.addSubProtocol(subprotocol);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::atomic<uint64_t> receivedCount(0);
 | 
				
			||||||
 | 
					        uint64_t receivedCountTotal(0);
 | 
				
			||||||
 | 
					        uint64_t receivedCountPerSecs(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Setup a callback to be fired (in a background thread, watch out for race conditions !)
 | 
				
			||||||
 | 
					        // when a message or an event (open, close, error) is received
 | 
				
			||||||
 | 
					        webSocket.setOnMessageCallback([&webSocket, &receivedCount, &sendMsg, noSend, binaryMode](
 | 
				
			||||||
 | 
					                                           const ix::WebSocketMessagePtr& msg) {
 | 
				
			||||||
 | 
					            if (msg->type == ix::WebSocketMessageType::Message)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (!noSend)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    webSocket.send(msg->str, msg->binary);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                receivedCount++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (msg->type == ix::WebSocketMessageType::Open)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                spdlog::info("ws_echo_client: connected");
 | 
				
			||||||
 | 
					                spdlog::info("Uri: {}", msg->openInfo.uri);
 | 
				
			||||||
 | 
					                spdlog::info("Headers:");
 | 
				
			||||||
 | 
					                for (auto it : msg->openInfo.headers)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    spdlog::info("{}: {}", it.first, it.second);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                webSocket.send(sendMsg, binaryMode);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (msg->type == ix::WebSocketMessageType::Pong)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                spdlog::info("Received pong {}", msg->str);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto timer = [&receivedCount, &receivedCountTotal, &receivedCountPerSecs] {
 | 
				
			||||||
 | 
					            setThreadName("Timer");
 | 
				
			||||||
 | 
					            while (true)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                //
 | 
				
			||||||
 | 
					                // We cannot write to sentCount and receivedCount
 | 
				
			||||||
 | 
					                // as those are used externally, so we need to introduce
 | 
				
			||||||
 | 
					                // our own counters
 | 
				
			||||||
 | 
					                //
 | 
				
			||||||
 | 
					                std::stringstream ss;
 | 
				
			||||||
 | 
					                ss << "messages received: " << receivedCountPerSecs << " per second "
 | 
				
			||||||
 | 
					                   << receivedCountTotal << " total";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                CoreLogger::info(ss.str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                receivedCountPerSecs = receivedCount - receivedCountTotal;
 | 
				
			||||||
 | 
					                receivedCountTotal += receivedCountPerSecs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                auto duration = std::chrono::seconds(1);
 | 
				
			||||||
 | 
					                std::this_thread::sleep_for(duration);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::thread t1(timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Now that our callback is setup, we can start our background thread and receive messages
 | 
				
			||||||
 | 
					        std::cout << "Connecting to " << url << "..." << std::endl;
 | 
				
			||||||
 | 
					        webSocket.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Send a message to the server (default to TEXT mode)
 | 
				
			||||||
 | 
					        webSocket.send("hello world");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (true)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string text;
 | 
				
			||||||
 | 
					            std::cout << "> " << std::flush;
 | 
				
			||||||
 | 
					            std::getline(std::cin, text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            webSocket.send(text);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					} // namespace ix
 | 
				
			||||||
							
								
								
									
										108
									
								
								ws/ws_push_server.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								ws/ws_push_server.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws_push_server.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXNetSystem.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocketServer.h>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ws_push_server(int port,
 | 
				
			||||||
 | 
					                       bool greetings,
 | 
				
			||||||
 | 
					                       const std::string& hostname,
 | 
				
			||||||
 | 
					                       const ix::SocketTLSOptions& tlsOptions,
 | 
				
			||||||
 | 
					                       bool ipv6,
 | 
				
			||||||
 | 
					                       bool disablePerMessageDeflate,
 | 
				
			||||||
 | 
					                       bool disablePong,
 | 
				
			||||||
 | 
					                       const std::string& sendMsg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        spdlog::info("Listening on {}:{}", hostname, port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ix::WebSocketServer server(port,
 | 
				
			||||||
 | 
					                                   hostname,
 | 
				
			||||||
 | 
					                                   SocketServer::kDefaultTcpBacklog,
 | 
				
			||||||
 | 
					                                   SocketServer::kDefaultMaxConnections,
 | 
				
			||||||
 | 
					                                   WebSocketServer::kDefaultHandShakeTimeoutSecs,
 | 
				
			||||||
 | 
					                                   (ipv6) ? AF_INET6 : AF_INET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.setTLSOptions(tlsOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (disablePerMessageDeflate)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            spdlog::info("Disable per message deflate");
 | 
				
			||||||
 | 
					            server.disablePerMessageDeflate();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (disablePong)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            spdlog::info("Disable responding to PING messages with PONG");
 | 
				
			||||||
 | 
					            server.disablePong();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.setOnClientMessageCallback(
 | 
				
			||||||
 | 
					            [greetings, &sendMsg](std::shared_ptr<ConnectionState> connectionState,
 | 
				
			||||||
 | 
					                                  ConnectionInfo& connectionInfo,
 | 
				
			||||||
 | 
					                                  WebSocket& webSocket,
 | 
				
			||||||
 | 
					                                  const WebSocketMessagePtr& msg) {
 | 
				
			||||||
 | 
					                auto remoteIp = connectionInfo.remoteIp;
 | 
				
			||||||
 | 
					                if (msg->type == ix::WebSocketMessageType::Open)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    spdlog::info("New connection");
 | 
				
			||||||
 | 
					                    spdlog::info("remote ip: {}", remoteIp);
 | 
				
			||||||
 | 
					                    spdlog::info("id: {}", connectionState->getId());
 | 
				
			||||||
 | 
					                    spdlog::info("Uri: {}", msg->openInfo.uri);
 | 
				
			||||||
 | 
					                    spdlog::info("Headers:");
 | 
				
			||||||
 | 
					                    for (auto it : msg->openInfo.headers)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        spdlog::info("{}: {}", it.first, it.second);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (greetings)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        webSocket.sendText("Welcome !");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    bool binary = false;
 | 
				
			||||||
 | 
					                    while (true)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        webSocket.send(sendMsg, binary);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (msg->type == ix::WebSocketMessageType::Close)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    spdlog::info("Closed connection: client id {} code {} reason {}",
 | 
				
			||||||
 | 
					                                 connectionState->getId(),
 | 
				
			||||||
 | 
					                                 msg->closeInfo.code,
 | 
				
			||||||
 | 
					                                 msg->closeInfo.reason);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (msg->type == ix::WebSocketMessageType::Error)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    spdlog::error("Connection error: {}", msg->errorInfo.reason);
 | 
				
			||||||
 | 
					                    spdlog::error("#retries: {}", msg->errorInfo.retries);
 | 
				
			||||||
 | 
					                    spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time);
 | 
				
			||||||
 | 
					                    spdlog::error("HTTP Status: {}", msg->errorInfo.http_status);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (msg->type == ix::WebSocketMessageType::Message)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    spdlog::info("Received {} bytes", msg->wireSize);
 | 
				
			||||||
 | 
					                    webSocket.send(msg->str, msg->binary);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto res = server.listen();
 | 
				
			||||||
 | 
					        if (!res.first)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            spdlog::error(res.second);
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.start();
 | 
				
			||||||
 | 
					        server.wait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					} // namespace ix
 | 
				
			||||||
		Reference in New Issue
	
	Block a user