Compare commits
	
		
			5 Commits
		
	
	
		
			feature/kq
			...
			feature/zl
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 5036e338c7 | ||
|  | 80fb8cfb59 | ||
|  | 43c0ae0812 | ||
|  | dcc447ec4a | ||
|  | 5127094f0e | 
							
								
								
									
										13
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +0,0 @@ | |||||||
| name: linux |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   linux: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: make test_make |  | ||||||
|       run: make test_make |  | ||||||
							
								
								
									
										15
									
								
								.github/workflows/unittest_mac_tsan_mbedtls.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/unittest_mac_tsan_mbedtls.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,15 +0,0 @@ | |||||||
| name: mac_tsan_mbedtls |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   mac_tsan_mbedtls: |  | ||||||
|     runs-on: macOS-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: install mbedtls |  | ||||||
|       run: brew install mbedtls |  | ||||||
|     - name: make test |  | ||||||
|       run: make test_tsan_mbedtls |  | ||||||
							
								
								
									
										15
									
								
								.github/workflows/unittest_mac_tsan_openssl.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/unittest_mac_tsan_openssl.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,15 +0,0 @@ | |||||||
| name: mac_tsan_openssl |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   mac_tsan_openssl: |  | ||||||
|     runs-on: macOS-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: install openssl |  | ||||||
|       run: brew install openssl@1.1 |  | ||||||
|     - name: make test |  | ||||||
|       run: make test_tsan_openssl |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| name: mac_tsan_sectransport |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   mac_tsan_sectransport: |  | ||||||
|     runs-on: macOS-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: make test_tsan |  | ||||||
|       run: make test_tsan |  | ||||||
							
								
								
									
										38
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,38 +0,0 @@ | |||||||
| name: uwp |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   uwp: |  | ||||||
|     runs-on: windows-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master |  | ||||||
|     - run: | |  | ||||||
|         mkdir 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 -DUSE_ZLIB=0 .. |  | ||||||
|     - run: cmake --build build |  | ||||||
|  |  | ||||||
| # |  | ||||||
| #   Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg |  | ||||||
| # |  | ||||||
| #   windows_openssl: |  | ||||||
| #     runs-on: windows-latest |  | ||||||
| #     steps: |  | ||||||
| #     - uses: actions/checkout@v1 |  | ||||||
| #     - uses: seanmiddleditch/gha-setup-vsdevenv@master |  | ||||||
| #     - run: | |  | ||||||
| #         vcpkg install zlib:x64-windows |  | ||||||
| #         vcpkg install openssl:x64-windows |  | ||||||
| #     - run: | |  | ||||||
| #         mkdir build |  | ||||||
| #         cd build |  | ||||||
| #         cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_OPEN_SSL=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. |  | ||||||
| #     - run: cmake --build build |  | ||||||
| #  |  | ||||||
| #     # Running the unittest does not work, the binary cannot be found |  | ||||||
| #     #- run: ../build/test/ixwebsocket_unittest.exe |  | ||||||
| #     # working-directory: test |  | ||||||
							
								
								
									
										5
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,6 +15,5 @@ jobs: | |||||||
|         cd build |         cd build | ||||||
|         cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 .. |         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 | ||||||
| #- run: ../build/test/ixwebsocket_unittest.exe |       working-directory: test | ||||||
| # working-directory: test |  | ||||||
|   | |||||||
| @@ -194,11 +194,13 @@ option(USE_ZLIB "Enable zlib support" TRUE) | |||||||
|  |  | ||||||
| if (USE_ZLIB) | 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) | ||||||
|  |   if (ZLIB_FOUND) | ||||||
|     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) |     target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB) | ||||||
|  |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| if (WIN32) | if (WIN32) | ||||||
|   | |||||||
| @@ -1,23 +1,6 @@ | |||||||
| # 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,7 +17,6 @@ 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 | ||||||
|   | |||||||
| @@ -1,37 +0,0 @@ | |||||||
|  |  | ||||||
| ## 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 |  | ||||||
| ``` |  | ||||||
| @@ -35,122 +35,19 @@ 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 | ||||||
| @@ -245,7 +142,6 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return pollResult; |         return pollResult; | ||||||
| #endif |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PollResultType Socket::isReadyToRead(int timeoutMs) |     PollResultType Socket::isReadyToRead(int timeoutMs) | ||||||
| @@ -256,7 +152,7 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         bool readyToRead = true; |         bool readyToRead = true; | ||||||
|         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt, _kqueuefd); |         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PollResultType Socket::isReadyToWrite(int timeoutMs) |     PollResultType Socket::isReadyToWrite(int timeoutMs) | ||||||
| @@ -267,7 +163,7 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         bool readyToRead = false; |         bool readyToRead = false; | ||||||
|         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt, _kqueuefd); |         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // 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,13 +13,6 @@ | |||||||
| #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; | ||||||
| @@ -101,8 +94,7 @@ 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 | ||||||
| @@ -122,9 +114,5 @@ 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,10 +66,7 @@ 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,11 +259,8 @@ 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, kqueuefd); |                 Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt); | ||||||
|             ::close(kqueuefd); |  | ||||||
|  |  | ||||||
|             if (pollResult == PollResultType::Error) |             if (pollResult == PollResultType::Error) | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #define IX_WEBSOCKET_VERSION "10.1.5" | #define IX_WEBSOCKET_VERSION "10.1.1" | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								makefile
									
									
									
									
									
								
							| @@ -34,10 +34,7 @@ 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 -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=0 -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && 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) | ||||||
|   | |||||||
| @@ -1,189 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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
									
									
								
							
							
						
						
									
										2
									
								
								test/compatibility/csharp/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +0,0 @@ | |||||||
| bin |  | ||||||
| obj |  | ||||||
| @@ -1,99 +0,0 @@ | |||||||
| // |  | ||||||
| // 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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| <Project Sdk="Microsoft.NET.Sdk"> |  | ||||||
|   <PropertyGroup> |  | ||||||
|     <OutputType>Exe</OutputType> |  | ||||||
|     <TargetFramework>netcoreapp3.1</TargetFramework> |  | ||||||
|   </PropertyGroup> |  | ||||||
| </Project> |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| // |  | ||||||
| // 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; |  | ||||||
| }); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1,44 +0,0 @@ | |||||||
| #!/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,9 +50,7 @@ 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,7 +125,6 @@ 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; | ||||||
| @@ -148,7 +147,6 @@ 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; | ||||||
| @@ -245,19 +243,6 @@ 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(); | ||||||
| @@ -267,25 +252,12 @@ 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("-q", quiet, "Quiet / only display warnings and errors"); |     echoServerApp->add_flag("-g", greetings, "Verbose"); | ||||||
|     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"); | ||||||
| @@ -505,11 +477,6 @@ 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; | ||||||
| @@ -531,33 +498,11 @@ 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,15 +35,6 @@ 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); | ||||||
| @@ -63,15 +54,6 @@ 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) | ||||||
|             { |             { | ||||||
|                 spdlog::info("ws_connect: connected"); |                 log("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) | ||||||
|   | |||||||
| @@ -1,121 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  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 |  | ||||||
| @@ -1,108 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  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