Compare commits

..

1386 Commits

Author SHA1 Message Date
Benjamin Sergeant
9884c325dd makefile: remove some install targets 2024-05-17 02:02:58 -07:00
teejusb
c27f5a94bd
Version check mbedtls instead of introducing a new define (#516) 2024-05-08 11:02:00 -07:00
Sergey Fedorov
2d47af89cf
IXSocket.h: add missing <sys/types.h> for macOS (#512) 2024-05-08 07:33:37 -07:00
Sergey Fedorov
c106e6cb24
Minor fixes for < 10.6 (#515)
* Fix for missing AI_NUMERICSERV on < 10.6

* Do not use pthread_setname_np on < 10.6
2024-05-08 07:33:23 -07:00
teejusb
1d210c0139
Initialize the PSA Crypto API if requested (#514) 2024-04-29 21:12:56 -07:00
Benjamin Sergeant
dc8807ec9d changelog 2024-03-27 22:12:37 -07:00
Benjamin Sergeant
03e5a6970f bump version in CMakeLists.txt 2024-03-27 22:11:15 -07:00
Olivia (Zoe)
38d6da7755
Fix bad version variable (#510) 2024-03-27 22:06:44 -07:00
Benjamin Sergeant
9ef61bf224 bump version 2024-03-27 22:06:32 -07:00
Benjamin Sergeant
93e673da9f Merge branch 'master' of github.com:machinezone/IXWebSocket 2024-03-27 22:03:54 -07:00
Benjamin Sergeant
92beef8348 avoid some object copies 2024-03-27 22:03:26 -07:00
Giuseppe Penone
755d98d918
Support URLs with no slash before the question mark (#507)
* Support Url No Slash Before Question Mark

* Support Url No Slash Before Question Mark

* unit test fix

---------

Co-authored-by: Giuseppe Penone <giuseppe.penone@delonghigroup.com>
2024-03-18 22:26:45 -07:00
CryptoManiac
98b4828e93
Update IXSelectInterruptPipe.cpp (#502)
Valgrind keeps complaining that close() on the invalid descriptor -1 is happening here. I suppose that close shouldn't be called when the descriptor value is known to be invalid. It's not a fatal error or something, but this practice is able to create a lot of flood in the logs.
2024-03-18 22:24:11 -07:00
Paul Whiting
39e085bebc
Fix MbedTLS disconnect handling. (#500) 2024-03-18 22:23:14 -07:00
Daniel Wymark
70602c4e6b
Add ping interval to constructor params for WebSocketServer (#497)
* Update .gitignore for CLion compatibility

* Add pingIntervalSeconds to constructor for WebSocketServer

* Add Heartbeat section to WebSocketServer usage documentation

* Fix typo
2024-03-12 09:46:27 -07:00
bsergean
c5a02f1066
Update README.md 2024-02-10 22:03:22 -08:00
bsergean
e03c0be8a4
Update unittest_windows_gcc.yml (#494)
* Update unittest_windows_gcc.yml

* Update unittest_windows_gcc.yml
2023-11-20 22:28:12 -08:00
RH
3b66efbb6a
Fix C++/WinRT compile issue (#493) 2023-11-15 10:40:49 -08:00
arenevier
f29906c72f
Allow building without rtti (#487)
Since factory returns a ProxyConnectionState, setOnConnectionCallback
will be a ProxyConnectionState. The code already makes that assumption,
since it does not check of state return value. Using a
static_pointer_cast will allow the library to be build with rtti.
2023-10-13 19:56:24 -07:00
arenevier
872f516ede
allow building when cpp exceptions are disabled (#489)
IXWebSocket needs exceptions support because of the use of stoi. In
order to build when cpp exceptions are disabled, we can use strtol.
2023-10-13 19:55:26 -07:00
Benjamin Sergeant
014d43eb13 stop building mingw ; if someone wants to maintain this port please reach out ! 2023-10-13 19:54:25 -07:00
bsergean
d77067e50f
Update window gcc action to latest setup_mingw 2023-10-13 19:39:41 -07:00
Michael M
ed5b1a0895
Fix links & update info in README (#485) 2023-09-15 22:36:52 -07:00
David Capello
ef57e3a2b1
Fix hanging of WebSocket::stop() waiting for its thread to join (#481)
This might happen in some special cases where
WebSocketTransport::sendOnSocket() fails, closes the socket, and set
the ready state to CLOSED, but WebSocket::run() stills wait the
interrupt event to happen.

Possibly related to #474
2023-09-01 08:11:36 -07:00
Glenn Engel
28832f8732
Fix #286 - http response headers overwritten with request headers (#483) 2023-09-01 08:11:07 -07:00
lanthora
0dd284267a
Fix MinGW build warning (#482) 2023-09-01 08:00:35 -07:00
lanthora
a7019631b7
Fix server empty thread name (#478) 2023-08-01 22:16:43 -07:00
Cheney Wang
632ee31509
Update IXSocketMbedTLS.cpp (#471) 2023-06-22 14:12:51 -07:00
Benjamin Sergeant
688af99747 bump version 2023-06-05 10:06:53 -07:00
lanthora
397bb5d18a
Fix version in CMakeLists.txt (#467)
* Fix version in CMakeLists.txt

* Disable IXHttpClientTest
2023-06-05 10:03:17 -07:00
lanthora
f79c64ae97
Add a reference to Candy in the README (#462) 2023-05-04 00:03:28 -07:00
lanthora
bc765e73a3
Add pkgconfig file (#459) 2023-04-25 11:54:25 -07:00
bsergean
dfa10df5ae
Set an origin header in websocket and http clients (#455) 2023-04-01 12:19:38 -07:00
Benjamin Sergeant
eb9a7bed76 fix warning about member variable initialization order + minor makefile build fix 2023-04-01 09:03:39 -07:00
Numendacil
d20864d7d1
Replace CMAKE_BINARY_DIR with CMAKE_CURRENT_BINARY_DIR (#454) 2023-03-29 20:59:39 -07:00
ouwou
f184a7adef
fix incorrect closures with code 1011 Internal error (#450)
* fix incorrect closures with code 1011 Internal error

* enable IXWebSocketCloseTest
2023-03-29 20:45:29 -07:00
Thefrank
dc7b986e10
Build fix for FreeBSD (#449) 2023-03-13 09:36:25 -07:00
itytophile
1e3560014f
Prevent deadlock when server is stopping (#426) 2023-02-25 14:41:05 -08:00
Azamat H. Hackimov
9157873f5b
Fix compilation on GCC-13 (#443)
* Fix compilation on GCC-13

GCC-13 changes internal cstdint includes, and now files that uses
standart integer types should directly include cstdint header.

See: https://gcc.gnu.org/gcc-13/porting_to.html#header-dep-changes
Bug: https://bugs.gentoo.org/865117
Bug: https://bugs.gentoo.org/895440

* Convert line endings to Unix format
2023-02-25 13:50:35 -08:00
YuHuanTin
aa2ca19895
Added support for setting custom ping messages with support for any 'ping' message type (binary ping message, text ping message) (#438)
* Added support for setting custom ping messages with support for any 'ping' message type (binary ping message, text ping message)

* Add default value

---------

Co-authored-by: YuHuanTin <2@>
2023-02-23 08:29:07 -08:00
Vol-Alex
6cc21f3658
HTTP should contain port in 'Host' header (#427) 2023-02-11 15:27:40 -08:00
itytophile
679ce519dd
Fix DNSLookup memory leak (#422)
* Fix memory leak with shared_ptr and -fsanitize=address

* Replace addrinfo* by shared_ptr

* fsanitize=address only on Linux

* Add USE_WS Linux CI test

* Remove fsanitize from the cmake files

* Remove USE_WS in linux test suite
2022-12-22 17:13:51 -08:00
crjc
a5d4911a16
Fix macos link error (#423)
* Fix macos link error

* Update CMakeLists.txt

* Update CMakeLists.txt
2022-12-22 09:16:10 -08:00
TheArtfulBodger
b0fd119d14
Host HTTP and WS on the same port (#414)
* Quick hack to host HTTP and WS on the same port #373

* Quick hack to host HTTP and WS on the same port #373 - simplify code

* ran clang-format

Co-authored-by: En Shih <seanstone5923@gmail.com>
Co-authored-by: The Artful Bodger <TheArtfulBodger@users.noreply.github.com>
2022-11-05 18:53:11 -07:00
李浩能
472cf68c31
add Content-Type support (#405) 2022-10-12 06:43:05 -07:00
Robin Sommer
1e46466114
Add option to disable hostname check (#399)
* Suppress compiler warnings about unused elements.

* Enable CMake's compilation database.

* Add TLS option to disable checking a certificate's host name.

* Add `--disable-hostname-validation` to `ws`.

* Add test for disabling hostname validation.
2022-10-12 06:41:32 -07:00
bsergean
0b8b5608dc
Update doc to talk about binding to 0.0.0.0 2022-09-08 14:58:19 -07:00
Seizure Salad
20a028e2ae
Fix spelling mistake (#401) 2022-08-02 13:28:59 -07:00
Benjamin Sergeant
8d7b557be6
Delete stale.yml
Remove the stale github action.
2022-06-30 10:41:08 +02:00
Benjamin Sergeant
e417e63605
Update CHANGELOG.md 2022-05-13 10:45:46 -07:00
Benjamin Sergeant
7b1524d7ec
Update IXWebSocketVersion.h 2022-05-13 10:43:32 -07:00
Max Weisel
e8048ad826
BoringSSL does not allow setting the hostname with a null-terminated string. The length is always required: https://boringssl.googlesource.com/boringssl/+/master/crypto/x509/x509_vpm.c#93 (#391) 2022-05-05 08:11:18 -07:00
Benjamin Sergeant
2b40a30c8f
Update README.md 2022-05-02 09:34:43 -07:00
Benjamin Sergeant
d7bfe89e43
Set shorter thread names (#379) 2022-04-30 10:18:20 -07:00
Benjamin Sergeant
84aa652846
Set shorter thread names (#379) 2022-04-30 10:16:53 -07:00
Robin Sommer
edb6ded99f
Fix Sec-WebSocket-Key to contain valid Base64. (#389)
The generated header only "looked like" Base64, but if the other side
actually tried to decode it as such, it could fail. This change fixes
that to always generate a valid Base64 value.

The Base64 code is copied from
https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594.
2022-04-29 00:05:06 -07:00
Benjamin Sergeant
2f560ff4c0
Update IXWebSocketVersion.h 2022-04-28 23:56:40 -07:00
Benjamin Sergeant
002d9c8985
Update ixwebsocket-config.cmake.in (#390) 2022-04-28 23:56:00 -07:00
Benjamin Sergeant
6d8495bd73
Update CHANGELOG.md 2022-04-23 22:53:36 -07:00
Benjamin Sergeant
b8563eddd1
11.4.1 2022-04-23 22:52:32 -07:00
Cheney Wang
46bd2aa4a1
vcpkg zlib dep fix (#385)
* vcpkg zlib dep fix

* Use cmake.in file instead of write file directly

Co-authored-by: Cheney-Wang <v-xincwa@microsoft.com>
2022-04-23 18:16:13 -07:00
Benjamin Sergeant
4420bc70b5
Revert "Export static symbols when building ws with shared library (#370)" (#383)
This reverts commit a3d2fa4b7ee65a40cdabbe6258db75c3866d5908.
2022-04-12 08:55:43 -07:00
Benjamin Sergeant
20921f341a
Update README.md 2022-03-28 22:04:27 -07:00
Benjamin Sergeant
2829c62ef9
Fix error handling after calling X509_NAME_get_index_by_NID
This should fix #376
2022-03-27 19:14:40 -07:00
Anton Ivlev
a3d2fa4b7e
Export static symbols when building ws with shared library (#370) 2022-03-19 11:41:40 -07:00
Benjamin Sergeant
f7eb3688dd
Update IXExponentialBackoffTest.cpp 2022-02-17 09:17:47 -08:00
Benjamin Sergeant
7360333aca
Handle overflow in IXExponentialBackoff.cpp 2022-02-17 09:17:02 -08:00
Benjamin Sergeant
90f19e0280
Reference new IXExponentialBackoffTest test in CMakeLists.txt 2022-02-17 09:08:49 -08:00
Benjamin Sergeant
b72f81540b
Create IXExponentialBackoffTest.cpp 2022-02-17 09:07:46 -08:00
Benjamin Sergeant
a77fd2d698
Update catch to v2.13.8 (#365) 2022-02-17 08:38:46 -08:00
flagarde
e12a6ddbdd
Update IXSocketConnect.cpp (#356)
Part of the code is never executed
warning raised by clang
2022-02-10 20:50:55 -08:00
flagarde
127cc4a023
Fix for MINGW32 and clang on windows (#352) (#357)
* Update IXSocket.h

Avoid "conflicting declaration 'typedef SSIZE_T ssize_t'"

* Update IXUdpSocket.h

* Update IXNetSystem.cpp

ENOSPC and EAFNOSUPPORT are not defined for clang on windows
2022-02-10 20:49:33 -08:00
Benjamin Sergeant
7711cb1ae7
Feature/no libdeflate (#360)
remove libdeflate code in gzip codec
2022-02-10 20:47:32 -08:00
Martin Natano
db7057de69
Add support for streaming transfers (#353)
This change adds onChunkCallback to the request. If defined it will be
called repeatedly with the incoming data. This allows to process data on
the go or write it to disk instead of accumulating the data in memory.
2022-01-31 21:54:32 -08:00
Benjamin Sergeant
c28b569535
Edit doc about thread safety, fix #350 2022-01-28 16:27:45 -08:00
flagarde
8d661b8e81
Use Threads::Threads target (#349) 2022-01-16 17:39:21 -08:00
flagarde
a951bc9cae
Add an alias for ixwebsocket (#348) 2022-01-16 17:36:38 -08:00
Andreas Hausladen
b5cf33a582
Introduction of IXWebSocketSendData (#347)
* Introduction of IXWebSocketSendData that makes it possible to not only send std::string but also std::vector<char/uint8_t> and char* without copying them to a std::string first.

Add a sendUtf8Text() method that doesn't check for invalid UTF-8 characters. The caller must guarantee that the string only contains valid UTF-8 characters.

* Updated usage.md: sendUtf8Text() and IXWebSocketSendData
2022-01-10 10:34:24 -08:00
Andreas Hausladen
2bc3afcf6c
Fixed missing header for gcc (9.3.0) compilation (#346) 2022-01-06 19:27:00 -08:00
Benjamin Sergeant
60563d88f2
Feature/11.4.0 (#344)
* mbedls system certs

* missing curly brace ...

* windows uwp for appveyor

* try again uwp

* update version and changelog

* revert odd change in test/IXSocketTest.cpp

Co-authored-by: Benjamin Sergeant <bsergeant@mz.com>
2022-01-05 10:43:18 -08:00
Andreas Hausladen
1f2895a469
Win wsa select event (#342)
* Fix #323: Missing SelectInterrupt implementation for Windows

Using WSAEventSelect, WSAWaitForMultipleEvents and WSAEnumNetworkEvents to emulate poll() with an interrupt-event.

* Cleanup

* Fixed incomplete comment.

* Switched ifdefs to support other Unixes with pipe file descriptors

* Fixed: SelectInterrupt fallback code for getFd()==-1 && getEvent()==nullptr converted a PollResultType::Timeout into a ReadyForRead causing the HttpClient to fail because it uses a hard-coded "SelectInterrupt" instance that doesn't implement getFd() and getEvent().

* Fixed gcc compile errors

* - HttpClient now uses the SelectInterruptFactory
- Fixed wrong ix::poll result when using Windows WSA functions

* We must deselect the networkevents from the socket event. Otherwise the socket will report states that aren't there.
2022-01-05 10:21:33 -08:00
Andreas Hausladen
9f00428d57
Fix "HTTP/1.1 400 Illegal character CNTL=0xf" caused by serverMaxWindowBits/clientMaxWindowBits being uint8_t (signed char). (#341) 2022-01-04 12:25:18 -08:00
CryptoManiac
f53b2f8878
Export symbols into .def files on MSVC (#339)
Fix #335
2022-01-04 12:13:38 -08:00
CryptoManiac
47d0b70ebf
Include <cerrno> to provide standard error constants (#338)
See https://en.cppreference.com/w/cpp/header/cerrno for additional details. Some of used constants are defined in this header.

Inclusion is necessary to avoid these errors:

```
/home/user/IXWebSocket/ixwebsocket/IXNetSystem.cpp:189:30: error: use of undeclared identifier 'EAFNOSUPPORT'
            default: errno = EAFNOSUPPORT; return 0;
                             ^
/home/user/IXWebSocket/ixwebsocket/IXNetSystem.cpp:191:17: error: use of undeclared identifier 'ENOSPC'
        errno = ENOSPC;
                ^
/home/user/IXWebSocket/ixwebsocket/IXNetSystem.cpp:175:25: warning: implicit conversion loses integer precision: 'size_t' (aka 'unsigned long long') to 'int' [-Wshorten-64-to-32]
                    j = strspn(buf + i, ":0");
                      ~ ^~~~~~~~~~~~~~~~~~~~~
/home/user/IXWebSocket/ixwebsocket/IXNetSystem.cpp:234:21: error: use of undeclared identifier 'EAFNOSUPPORT'
            errno = EAFNOSUPPORT;
                    ^
2 warnings and 3 errors generated.
```
2022-01-04 12:13:19 -08:00
Benjamin Sergeant
8c15405ed0
Add a reference to NovaCoin in the README 2021-12-22 22:52:53 -08:00
svost
5457217503
Improved compatibility - fix mingw crossbuild (#337) 2021-12-22 22:48:20 -08:00
Martin Natano
66cd29e747
Allow to cancel asynchronous HTTP requests (#332)
Usage:

	auto args = this->httpClient.createRequest(url, method);
	httpClient.performRequest(args, ...);
	[...]
	// Oops, we don't actually want to complete the request!
	args->cancel = true;
2021-12-20 23:01:55 -08:00
Matthew Gordon
688f85fda6
Fix errors in example code. (#336) 2021-12-20 22:59:15 -08:00
Benjamin Sergeant
71f73e5f6e
Update README (add another project using the lib) 2021-12-02 22:49:12 -08:00
Benjamin Sergeant
42db05a38b
Update mkdocs.yml 2021-11-24 09:05:54 -08:00
Benjamin Sergeant
6cce066021
Update index.md 2021-11-24 09:02:42 -08:00
Benjamin Sergeant
9c6dcb24a9
Update mkdocs.yml 2021-11-24 09:01:10 -08:00
Benjamin Sergeant
05a27c89e3
Update design.md 2021-11-24 09:00:23 -08:00
Benjamin Sergeant
23dbc8ae63
Update mkdocs.yml
Try a new version of the checkout action.
2021-11-24 08:56:14 -08:00
Benjamin Sergeant
5f2955ef78
Feature/version 11.3.2 (#329)
* mbedls system certs

* missing curly brace ...

* windows uwp for appveyor

* try again uwp

* bump version

* keep using local cacert.pem in unittest

* appveyor back to normal

* remove appveyor file

Co-authored-by: Benjamin Sergeant <bsergeant@mz.com>
2021-11-24 08:45:04 -08:00
flagarde
882081536c
Fix IXWebSocketMessage.h:35:15: warning: ‘webSocketMessageType’ may be used uninitialized in this function [-Wmaybe-uninitialized] (#328) 2021-11-24 08:33:09 -08:00
flagarde
74bb85efe9
Add getters (#327)
* Add getters for IXSocketServer class

* Add getters for IXHttpServer class

* Add getters for IXWebSocketServer class
2021-11-24 08:28:25 -08:00
ouwou
e66437b560
fix compilation under mingw64 (#325) 2021-11-16 15:22:51 -08:00
Benjamin Sergeant
97aa1f956a
Fix mbedtls-3.0 problem (#322)
* Fix mbedtls-3.0 problem

This cause CI to fail on macOS.

See this migration guide => https://github.com/ARMmbed/mbedtls/blob/development/docs/3.0-migration-guide.md

* cmake change find header file

* define macro for mbedtls >= 3

* update api call

* Update IXWebSocketVersion.h

* Update CHANGELOG.md
2021-10-22 11:10:58 -07:00
Enzo
3f1fc6906c
Correctly convert remote port bytecode to uint16 port number. (#321)
* Correctly convert remote port bytecode to uint16 port number.

Copied the network_to_host_short function from the ASIO library to convert the remote port byte code to a uint16 number.

* Switched from uint16_t to unsigned short to work in Windows

* Updated missed uint16_t to unsigned short
2021-10-22 10:23:43 -07:00
Benjamin Sergeant
9bbd1f1b30
Update package-lock.json
Fix a security issue with the node.js ws package, which is only used in testing.
2021-09-29 12:18:15 -07:00
Benjamin Sergeant
18c2b69633
Update build.md 2021-09-29 12:15:31 -07:00
Benjamin Sergeant
178f218374
Update IXWebSocketVersion.h 2021-09-20 18:19:29 -07:00
Benjamin Sergeant
6a1aa27b5f
Update CHANGELOG.md 2021-09-20 18:19:10 -07:00
Matthew Albrecht
e7f89ae529
Only find OpenSSL, MbedTLS, zlib if they have not already been found, make CMake install optional. (#307)
Co-authored-by: Benjamin Sergeant <bsergean@gmail.com>
2021-09-20 18:15:37 -07:00
Adrian Schollmeyer
cdeaf8e2be
use GNUInstallDirs in cmake (#318)
Signed-off-by: NexAdn <git@nexadn.de>

Co-authored-by: NexAdn <git@nexadn.de>
2021-09-20 18:11:46 -07:00
Benjamin Sergeant
dbafa0aa07
trigger github actions on pull requests (#313) 2021-08-13 05:07:24 -07:00
Benjamin Sergeant
3baf59a031 (ws) bump CLI command line parsing library from 1.8 to 2.0 2021-07-27 20:48:25 +02:00
Benjamin Sergeant
b5804c2082 cmake tweak 2021-06-09 10:46:31 -07:00
Benjamin Sergeant
30bcddb99f (ws) ws connect has a -g option to gzip decompress messages for API such as the websocket Huobi Global. 2021-06-08 09:49:27 -07:00
Benjamin Sergeant
47fd04e210 (websocket client + server) WebSocketMessage class tweak to fix unsafe patterns 2021-06-08 09:47:53 -07:00
Nikos Athanasiou
4f5b0c4f07
Noexcept ix web socket per message deflate options (#299)
* Fix unsafe calls and safeguard WebSocketMessage from being called w/
temporaries

* Use unnamed namespace to express internal linkage

* Avoid returning references that are mutex protected
Motivation for this MR

The antipattern of returning references to mutex protected members was
removed. Since a caller can hold the reference it would make all class
level locking meaningless.

Instead values are returned. The IXWebSocketPerMessageDeflateOptions
class was shrunk by 7 bytes (1 padding + 2*3) after changing the int
members to the used uint8_t; side effects of that were handled.

An inefficient "string -> int" was replaced by standard library. As
seen here http://coliru.stacked-crooked.com/a/46b5990bafb9c626 this
gives an order of magnitude better performance.

* noexcept string to integer conversion
2021-06-07 11:19:52 -07:00
Nikos Athanasiou
c2d497abc5
Avoid returning references that are mutex protected (#297)
* Fix unsafe calls and safeguard WebSocketMessage from being called w/
temporaries

* Use unnamed namespace to express internal linkage

* Avoid returning references that are mutex protected
Motivation for this MR

The antipattern of returning references to mutex protected members was
removed. Since a caller can hold the reference it would make all class
level locking meaningless.

Instead values are returned. The IXWebSocketPerMessageDeflateOptions
class was shrunk by 7 bytes (1 padding + 2*3) after changing the int
members to the used uint8_t; side effects of that were handled.

An inefficient "string -> int" was replaced by standard library. As
seen here http://coliru.stacked-crooked.com/a/46b5990bafb9c626 this
gives an order of magnitude better performance.
2021-06-05 11:23:18 -07:00
crjc
bbe2ae6dd3
fix: check the request's headers rather than the empty response's headers for User-Agent and Accept (#296) 2021-06-05 11:19:53 -07:00
Nikos Athanasiou
26897b2425
Fix unsafe calls and safeguard WebSocketMessage (#294)
* Fix unsafe calls and safeguard WebSocketMessage from being called w/
temporaries

* Use unnamed namespace to express internal linkage
2021-06-03 18:39:38 -07:00
Benjamin Sergeant
e3c98a03cc (websocket server) Handle and accept firefox browser special upgrade value (keep-alive, Upgrade) 2021-05-27 10:54:21 -07:00
Benjamin Sergeant
97fedf9482 (Windows) move EINVAL (re)definition from IXSocket.h to IXNetSystem.h (fix #289) 2021-05-27 10:54:21 -07:00
Benjamin Sergeant
ae187c0e98
Readme: Add mingw to build matrix 2021-05-18 11:15:21 -07:00
Benjamin Sergeant
0f21a20fe3 Move errno windows definitions to IXNetSystem.h 2021-05-17 19:04:02 -07:00
Benjamin Sergeant
54db6ec8bb add notes about ssl configuration in demo program 2021-05-09 13:45:01 -07:00
flagarde
0e0a748037
Remove warnings (#284) 2021-04-19 09:25:06 -07:00
Benjamin Sergeant
3b19b0eeca http client: DEL is not a verb, but DELETE is, fix #281 2021-04-04 23:27:28 -07:00
Bart
dbfe3104e8
Fixed example code for the new API of IXWebSocketServer (#279) 2021-03-26 23:55:34 -07:00
Benjamin Sergeant
68fd8c20d6 change CI mkdocs invocation 2021-03-25 11:12:59 -07:00
Benjamin Sergeant
d932af8568 (cmake) install IXUniquePtr.h 2021-03-25 10:55:59 -07:00
Benjamin Sergeant
3add6d4c2e (ssl + windows) missing include for CertOpenStore function 2021-03-24 08:03:56 -07:00
Benjamin Sergeant
0d7fb05567 (ixwebsocket) version bump 2021-03-23 21:54:54 -07:00
Benjamin Sergeant
bf1747ef18 (ixwebsocket) version bump 2021-03-23 21:54:15 -07:00
Benjamin Sergeant
5c9c05caff bump version 2021-03-23 21:52:49 -07:00
Benjamin Sergeant
2573ca151b CaseInsensitiveLess::NocaseCompare::operator mingw fix attempt 2021-03-23 21:21:36 -07:00
Benjamin Sergeant
c5b5fa82be use inet_* wrapper only on mingw 2021-03-23 21:13:18 -07:00
Benjamin Sergeant
80dff08304 invoke ctest manually on ci for windows + gcc builder 2021-03-23 21:07:26 -07:00
Benjamin Sergeant
24c2eae3d7 use inet_ntop and inet_pton musl implementations on all platforms 2021-03-23 20:53:19 -07:00
Benjamin Sergeant
449c5fa138 (ixwebsocket) add getMinWaitBetweenReconnectionRetries 2021-03-23 08:29:50 -07:00
Benjamin Sergeant
b6234ff908 compile errors due to missing changes for the introduction of setMinWaitBetweenReconnectionRetries and getMinWaitBetweenReconnectionRetries 2021-03-23 08:28:40 -07:00
Benjamin Sergeant
d26664fccc (ixwebsocket) New option to set the min wait between reconnection attempts. Still default to 1ms. (setMinWaitBetweenReconnectionRetries). 2021-03-23 07:33:48 -07:00
Benjamin Sergeant
def0243d6d (ws) initialize maxWaitBetweenReconnectionRetries to a non zero value ; a zero value was causing spurious reconnections attempts 2021-03-22 21:10:18 -07:00
Benjamin Sergeant
1410797d6f document -DBUILD_DEMO=ON 2021-03-22 08:51:58 -07:00
inull
2670187fe0
Adds demo building option. (#278) 2021-03-22 08:47:10 -07:00
Benjamin Sergeant
95359461d7 enable test on windows + gcc 2021-03-21 10:22:44 -07:00
Benjamin Sergeant
4d7b149649 mingw: cast fixes 2021-03-21 10:16:06 -07:00
Benjamin Sergeant
b29a37ce76 mingw: inet_ntop and inet_pton compilation fix, use correct parameter names 2021-03-21 09:50:15 -07:00
Benjamin Sergeant
9a4dfb40da mingw: add real implementation of inet_ntop and inet_pton taken from musl C library 2021-03-21 09:43:16 -07:00
Benjamin Sergeant
c4c344518d disable shared-libs on windows where test does not work yet 2021-03-20 10:18:10 -07:00
Benjamin Sergeant
d706a4a73e doc: document BUILD_SHARED_LIBS 2021-03-20 09:50:21 -07:00
Benjamin Sergeant
88970604e3 ixwebsocketserver::broadcast server to return a boolean to know whether the server could start/listen, and use that in ws 2021-03-19 11:52:41 -07:00
Benjamin Sergeant
7fee54464e WebSocketServer::listenAndStart: fix branch where we do not return an integer 2021-03-19 11:48:21 -07:00
Benjamin Sergeant
1c7634d075 ws: cannot use << with an std::vector 2021-03-19 11:43:29 -07:00
Benjamin Sergeant
99f9556aa9 ws + mingw: uses << operator to write file to disk in WebSocketReceiver::handleMessage 2021-03-19 11:39:14 -07:00
Benjamin Sergeant
39b2a3d6df ws curl -O mingw compile fix + detect when we cannot extract a filename from the url to save file to disk with -O option 2021-03-19 11:35:25 -07:00
Benjamin Sergeant
056b02a494 ws: WebSocketSender uses anonymous namespace load instead of its own method 2021-03-19 11:25:48 -07:00
Benjamin Sergeant
48166a9a72 mingw: fix compile errors with linenoise and fstream 2021-03-19 11:18:55 -07:00
Benjamin Sergeant
b36a2d1faa mingw compile fix / remove restrict in inet_* functions 2021-03-19 10:58:38 -07:00
Benjamin Sergeant
968cc5c1c4 reference wslay as alternative C websocket library 2021-03-19 08:05:01 -07:00
Benjamin Sergeant
0813eb1788 mention disablePerMessageDeflate in the doc 2021-03-16 09:56:08 -07:00
Benjamin Sergeant
cadb8336f2 add reference to Teleport which is using ixwebsockets 2021-03-16 09:10:36 -07:00
Benjamin Sergeant
7fd782f72f add WIN32_LEAN_AND_MEAN windows blip 2021-03-15 19:58:18 -07:00
Benjamin Sergeant
85bcdaaec3 stub inet_ntop and inet_pton function that mingw does not have 2021-03-14 14:25:40 -07:00
Benjamin Sergeant
461641f3d0 ci with unity build for windows + gcc 2021-03-14 13:23:16 -07:00
Benjamin Sergeant
2d65c27d11 rename windows+gcc unittest ci file 2021-03-14 13:18:09 -07:00
Benjamin Sergeant
6a7785d9d9 no set thread name on mingw 2021-03-13 19:02:20 -08:00
Benjamin Sergeant
78a670e0c8 more mingw quirks 2021-03-13 18:55:30 -08:00
Benjamin Sergeant
e63ac69ec6 mock poll struct and macro for mingw 2021-03-13 18:49:29 -08:00
Benjamin Sergeant
afa15d6dcf mingw build problem fix attempt 2021-03-13 18:31:42 -08:00
Benjamin Sergeant
432a202c07 use https://github.com/marketplace/actions/install-mingw 2021-03-13 18:21:46 -08:00
Benjamin Sergeant
d609370a85 change compiler 2021-03-13 18:16:21 -08:00
Benjamin Sergeant
bbe3a766f4 ci windows_gcc, disable zlib 2021-03-13 18:04:00 -08:00
Benjamin Sergeant
09d3520b66
Create windows_gcc.yml 2021-03-13 18:00:32 -08:00
Benjamin Sergeant
f090c7659b (ixwebsocket) Expose setHandshakeTimeout method 2021-03-07 19:29:28 -08:00
Benjamin Sergeant
7c195219cd reorder methods in IXWebSocket.h 2021-03-07 19:25:53 -08:00
Duncan Ogilvie
d739662a7c
Allow customizing the websocket handshake timeout (#264) 2021-03-07 19:23:43 -08:00
Laurent Amat
e7f7e470e2
Case sensitive link (#269) 2021-03-04 23:04:04 -08:00
Benjamin Sergeant
d239738ec6 add a.out to .gitignore 2021-02-19 13:51:10 -08:00
Benjamin Sergeant
c61975bf75 minor improvement to the main.cpp builtin example 2021-02-19 13:50:50 -08:00
Benjamin Sergeant
39cc0ed32f add comment in WebSocketServer::makeBroadcastServer 2021-01-28 21:04:18 -08:00
Benjamin Sergeant
22c3a7264e ws: document bug in ws dnslookup command 2021-01-21 15:07:47 -08:00
Benjamin Sergeant
ee5a2eb46e mention C++11 compatibility in the readme 2021-01-03 11:48:10 -08:00
Benjamin Sergeant
f6e34e4b34 stop using C++14 lambda capture init, code should be C++11 compatible 2021-01-03 11:44:05 -08:00
Benjamin Sergeant
d0359a1764 new makeBroadcastServer websocket server method for classic servers, used by ws 2021-01-03 11:24:12 -08:00
Benjamin Sergeant
8910ebcc3c enable some unittests on windows 2020-12-26 12:44:06 -08:00
Benjamin Sergeant
1ea3bc3666 no unity build on Windows because of a problem with spdlog 2020-12-25 17:31:30 -08:00
Benjamin Sergeant
fe92ad205d build with unity builds 2020-12-25 17:16:36 -08:00
Benjamin Sergeant
e4a1ac80c2 more stale references to ixcore 2020-12-25 16:32:52 -08:00
Benjamin Sergeant
e9dc7f7aed case sensitive file name 2020-12-25 16:29:47 -08:00
Benjamin Sergeant
cd82eed4ec simple cmake build error 2020-12-25 16:28:15 -08:00
Benjamin Sergeant
fabc07d598 (ws) trim ws dependencies no more ixcrypto and ixcore deps 2020-12-25 16:25:58 -08:00
Benjamin Sergeant
b89621fa78 remove ixbots / ixsnake / ixcobra / ixredis (which should go in their own standalone project 2020-12-25 15:32:15 -08:00
Benjamin Sergeant
049d1eec63 remove some un-needed third party code 2020-12-25 15:28:39 -08:00
Benjamin Sergeant
6122154f74 test only depend on ixcore and ixcrypto 2020-12-25 15:27:11 -08:00
Benjamin Sergeant
0b7919834a (ws) trim ws dependencies, only depends on ixcrypto and ixcore 2020-12-25 15:17:46 -08:00
Benjamin Sergeant
6035dd4c11 fix ci 2020-12-22 21:45:26 -08:00
Benjamin Sergeant
1d0432c8c5 (build) rename makefile to makefile.dev to ease cmake BuildExternal (fix #261) 2020-12-22 21:42:39 -08:00
Benjamin Sergeant
461a645704 (ws) Implement simple header based websocket authorization technique to reject 2020-12-17 22:42:14 -08:00
Benjamin Sergeant
93ad709dfd fix ws curl error message + some Windows warnings 2020-12-12 11:01:22 -08:00
Benjamin Sergeant
2fac4bd9ef s/autobahn/autoroute 2020-11-25 10:00:35 -08:00
Benjamin Sergeant
f566fb457b update readme 2020-11-25 09:59:27 -08:00
Benjamin Sergeant
75e9c84388 fix buggy message and remove un-needed include 2020-11-19 14:27:10 -08:00
Benjamin Sergeant
223cd41b3c (ixwebsocket) Handle EINTR return code in ix::poll and IXSelectInterrupt 2020-11-16 13:53:09 -08:00
Benjamin Sergeant
60aeaec734 hand EINTR in IXSelectInterruptPipe::notify and IXSelectInterruptPipe::read 2020-11-16 13:52:13 -08:00
Benjamin Sergeant
fcf114e2b2 Handle EINTR in ix::poll on Unix 2020-11-16 10:14:59 -08:00
Benjamin Sergeant
ea32c0e1ec fix ixsentry detection of std::regex 2020-11-16 09:19:08 -08:00
Benjamin Sergeant
866670a906 (ixwebsocket) Fix #252 / regression in 11.0.2 with string comparisons 2020-11-16 08:41:08 -08:00
Benjamin Sergeant
80432edbd0 add 2 new ubuntu docker files 2020-11-15 21:26:59 -08:00
Benjamin Sergeant
23606b45c7 C++11 compatible 2020-11-15 21:09:58 -08:00
Benjamin Sergeant
2aac0afca3 compile attempt 2 with old OpenSSL versions 2020-11-15 11:32:50 -08:00
Benjamin Sergeant
508d8c7253 compile attempt with old OpenSSL versions 2020-11-15 11:23:44 -08:00
Benjamin Sergeant
8f5134528b (ixwebsocket) use a C++11 compatible make_unique shim 2020-11-15 09:56:54 -08:00
Benjamin Sergeant
738c6040f7 fix memory leak in dns unittest 2020-11-12 13:07:31 -08:00
Benjamin Sergeant
1350e9b307 missing vector header 2020-11-11 21:47:07 -08:00
Benjamin Sergeant
4e2a40e031 (socket) replace a std::vector with an std::array used as a tmp buffer in Socket::readBytes 2020-11-11 21:39:31 -08:00
Benjamin Sergeant
594d2e194a linux asan / run test in verbose mode 2020-11-11 11:32:47 -08:00
Benjamin Sergeant
977a1ed7e1 link ordering fix for Linux 2020-11-11 19:23:51 +00:00
Benjamin Sergeant
8b3789af56 linux build fix attempt 2020-11-11 11:16:19 -08:00
Benjamin Sergeant
f60485d9c2 use ctest for testing 2020-11-11 11:11:34 -08:00
Benjamin Sergeant
b05b124cb3 update readme 2020-11-11 09:20:42 -08:00
Benjamin Sergeant
723c208f22 fix version 2020-11-11 09:18:03 -08:00
Benjamin Sergeant
21758f1183 (openssl security fix) in the client to server connection, peer verification is not done in all cases. See https://github.com/machinezone/IXWebSocket/pull/250 2020-11-11 09:16:14 -08:00
jb-gcx
422febf15d
(openssl) Always set verify peer when it is not disabled (#250) 2020-11-11 09:12:39 -08:00
Benjamin Sergeant
51ec32405d (docker) build docker container with zlib disabled 2020-11-07 11:22:52 -08:00
Benjamin Sergeant
6a90dc7259 (cmake) DEFLATE -> Deflate in CMake to stop warnings about casing 2020-11-07 09:40:54 -08:00
Benjamin Sergeant
262f32857f (ws autoroute) Display result in compliant way (AUTOROUTE IXWebSocket :: N ms) so that result can be parsed easily 2020-11-07 09:34:54 -08:00
Benjamin Sergeant
91fb3992ac (ws gunzip + IXGZipCodec) Can decompress gziped data with libdeflate. ws gunzip computed output filename was incorrect (was the extension aka gz) instead of the file without the extension. Also check whether the output file is writeable. 2020-11-07 09:34:54 -08:00
Lucca Nunes
e8b12feaeb
Update README.md (#249) 2020-11-01 18:17:12 -08:00
Benjamin Sergeant
730fbc5b31 unity build fixes 2020-10-26 19:18:55 -07:00
Benjamin Sergeant
d0562664ad (http code) With zlib disabled, some code should not be reached 2020-10-19 13:37:42 -07:00
SeanOMik
d9b4beff8b
Fix an issue with disabling zlib and getting linker errors from the http client. (#247)
* (http client) #ifdefs so we dont try to compress http requests with zlib

* (http client) Remove some #ifdefs for including zlib and removing fields
2020-10-19 13:36:04 -07:00
Benjamin Sergeant
b2f21840c6 (ws curl) Add support for --data-binary option, to set the request body. When present the request will be sent with the POST verb 2020-10-12 14:03:01 -07:00
Benjamin Sergeant
67cb48537a (http client + server + ws) Add support for compressing http client requests with gzip. --compress_request argument is used in ws to enable this. The Content-Encoding is set to gzip, and decoded on the server side if present. 2020-10-09 17:51:56 -07:00
Benjamin Sergeant
fa0408e70b (http client + server + ws) Add support for uploading files with ws -F foo=@filename, new -D http server option to debug incoming client requests, internal api changed for http POST, PUT and PATCH to supply an HttpFormDataParameters 2020-10-08 12:43:18 -07:00
Benjamin Sergeant
032ed9af9c IXExponentialBackoff.cpp: fix typo in source code file name in the header block 2020-10-05 10:39:11 -07:00
Benjamin Sergeant
dc84080401 Add support for gzip compression through libdeflate 2020-09-30 14:34:03 -07:00
Benjamin Sergeant
82e759732b (cmake) Stop using FetchContent cmake module to retrieve jsoncpp third party dependency 2020-09-30 14:24:04 -07:00
Benjamin Sergeant
61dbcc2b84 fix docker and linux build 2020-09-28 11:56:49 -07:00
Benjamin Sergeant
e61680ff0f linux build fix about memset not being found 2020-09-28 11:01:59 -07:00
Benjamin Sergeant
6f188a5131 (ws) add gzip and gunzip ws sub commands 2020-09-28 10:19:27 -07:00
Benjamin Sergeant
6077f86af8 (cmake) use FetchContent cmake module to retrieve jsoncpp third party dependency 2020-09-26 14:11:40 -07:00
Benjamin Sergeant
93167e3917 cmake / move FetchContent spdlog to a single place 2020-09-26 13:55:03 -07:00
Benjamin Sergeant
2526a94454 (cmake) use FetchContent cmake module to retrieve spdlog third party dependency 2020-09-26 13:51:19 -07:00
Benjamin Sergeant
97cc543e53 (cobra connection) retrieve cobra server connection id from the cobra handshake message and display it in ws clients, metrics publisher and bots 2020-09-22 09:30:19 -07:00
Benjamin Sergeant
62d220f49a (cobra 2 cobra) specify as an HTTP header which channel we will republish to 2020-09-22 08:55:21 -07:00
Benjamin Sergeant
49995e32f0 (cobra bots) change an error log to a warning log when reconnecting because no messages were received for a minute 2020-09-18 15:25:10 -07:00
Benjamin Sergeant
d525c28907 (cobra connection and bots) set an HTTP header when connecting to help with debugging bots 2020-09-18 15:11:20 -07:00
carr-7
39c84c7d51
Rename HttpResponse's payload to body (#245)
* rename payload to body

* Fixed ws cmd line tool to use the renamed body

Co-authored-by: Jay <jasoncarr@Jasons-MacBook-Pro.local>
2020-09-12 19:01:37 -07:00
Benjamin Sergeant
128bc0afa9 (http server) read body request when the Content-Length is specified + set timeout to read the request to 30 seconds max by default, and make it configurable as a constructor parameter 2020-09-12 14:17:06 -07:00
Benjamin Sergeant
b04e5c5529 http server: use socket->readBytes which reads in bulk instead of N calls to socket->readByte 2020-09-12 14:09:25 -07:00
Benjamin Sergeant
1e8c421d66 formatting 2020-09-12 13:55:27 -07:00
carr-7
72d6651ded
Read body in parseRequest for HttpServer (#244)
Co-authored-by: Jay <jasoncarr@Jasons-MacBook-Pro.local>
2020-09-12 13:53:56 -07:00
Benjamin Sergeant
a4e5d1b47a (ws) autoroute command exit on its own once all messages have been received 2020-09-09 18:01:38 -07:00
Benjamin Sergeant
9f51a54a83 (docker) ws docker file installs strace 2020-09-04 13:47:12 -07:00
Benjamin Sergeant
b74f7319c6 add a note to the readme about the fact that the MinGW compiler is not supported. close #242 2020-09-03 13:50:46 -07:00
Benjamin Sergeant
0ad66a27f2 Fix ws/ws.cpp:2875:10: warning: unused variable noSend [-Wunused-variable] 2020-09-03 09:17:52 -07:00
Benjamin Sergeant
a40003e85a (ws) echo_client command renamed to autoroute. Command exit once the server close the connection. push_server commands exit once N messages have been sent. 2020-09-03 09:13:23 -07:00
Benjamin Sergeant
5534a7fdf9 add a github action to publish a docker container for ws 2020-09-02 11:52:59 -07:00
Benjamin Sergeant
efb245278d unittest / switch from using the REQUIRE macro, which halts (and usually crash) the test to the CHECK macro in IXWebSocketChatTest.cpp 2020-08-31 13:56:45 -07:00
Benjamin Sergeant
5896d3740f (ws + cobra bots) add a cobra_to_cobra ws subcommand to subscribe to a channel and republish received events to a different channel 2020-08-31 13:45:00 -07:00
Benjamin Sergeant
73b9c0b89b (socket servers) merge the ConnectionInfo class with the ConnectionState one, which simplify all the server apis 2020-08-28 14:55:40 -07:00
Benjamin Sergeant
629c155044 (ws) fix silly compile error (missing ix:: namespace) 2020-08-26 14:30:58 -07:00
Benjamin Sergeant
08640d877f (ws) set the main thread name, to help with debugging in XCode, gdb, lldb etc... 2020-08-26 13:38:45 -07:00
Benjamin Sergeant
ed5c63144e (ws) cobra to python bot / take a module python name as argument foo.bar.baz instead of a path foo/bar/baz.py 2020-08-19 10:00:00 -07:00
Benjamin Sergeant
ee69aed2b0 (ws) on Linux with mbedtls, when the system ca certs are specified (the default) pick up sensible OS supplied paths (tested with CentOS and Alpine) 2020-08-19 09:31:57 -07:00
Benjamin Sergeant
fcb92f862d (ws push_server) on the server side, stop sending and close the connection when the remote end has disconnected 2020-08-18 14:09:27 -07:00
Benjamin Sergeant
e8e98e667d add ruby websocket bencharking code using
faye-websocket-ruby to receive messages as fast as possible
2020-08-18 13:45:53 -07:00
Benjamin Sergeant
e1502017ce (ixwebsocket) replace std::unique_ptr<unsigned char[]> with std::array for some fixed arrays (which are in C++11) 2020-08-17 16:48:26 -07:00
Benjamin Sergeant
72472f2899 IXWebSocketPerMessageDeflateCodec: use std::array instead of std::unique_ptr for a fixed size array 2020-08-17 16:36:24 -07:00
Benjamin Sergeant
42f71364ca IXHttpClient.cpp: use std::array instead of std::unique_ptr for a fixed size array 2020-08-17 16:25:55 -07:00
Benjamin Sergeant
3dabd3a556 (ws) merge all ws_*.cpp files into a single one to speedup compilation 2020-08-15 19:30:17 -07:00
Benjamin Sergeant
0498e2fa98 IXBench.h is missing a pragma once 2020-08-15 18:58:46 -07:00
Benjamin Sergeant
2aaf59651e (socket server) in the loop accepting connections, call select without a timeout on unix to avoid busy looping, and only wake up when a new connection happens 2020-08-15 18:32:59 -07:00
Benjamin Sergeant
cd4e51eacf (socket server) instead of busy looping with a sleep, only wake up the GC thread when a new thread will have to be joined, (we know that thanks to the ConnectionState OnSetTerminated callback 2020-08-15 16:24:35 -07:00
Benjamin Sergeant
785842de03 (socket server) add a callback to the ConnectionState to be invoked when the connection is terminated. This will be used by the SocketServer in the future to know on time that the associated connection thread can be terminated. 2020-08-15 16:03:40 -07:00
Benjamin Sergeant
261095fa12 (socket server) do not create a select interrupt object everytime when polling for notifications while waiting for new connections, instead use a persistent one which is a member variable 2020-08-15 15:28:15 -07:00
Benjamin Sergeant
ed2ed0f7ae (ixwebsocket client) handle HTTP redirects 2020-08-14 18:13:34 -07:00
Benjamin Sergeant
7ad5ead0f6 document the --config_path option in usage.md 2020-08-14 15:15:27 -07:00
Benjamin Sergeant
a8284e64e3 add a simple shell script to test websocket proxy 2020-08-14 15:09:34 -07:00
Benjamin Sergeant
5423a31d5a (ws) have more subcommand handle --pidfile, to write pid to a file 2020-08-14 15:09:12 -07:00
Benjamin Sergeant
53575f8d90 change makefile openssl target to use ninja and install ws 2020-08-14 15:08:37 -07:00
Benjamin Sergeant
d3bcbdac26 (ws) upgrade to latest version of nlohmann json (3.9.1 from 3.2.0) 2020-08-13 22:10:38 -07:00
Benjamin Sergeant
8c5b28adce (websocket proxy server) add ability to map different hosts to different websocket servers, using a json config file 2020-08-13 21:20:42 -07:00
Benjamin Sergeant
dcbafae35a (ws) on macOS, with OpenSSL or MbedTLS, use /etc/ssl/cert.pem as the system certs 2020-08-12 18:55:13 -07:00
Benjamin Sergeant
eb197edcec ws --version does not get printed with a log prefix 2020-08-12 18:44:47 -07:00
Benjamin Sergeant
b8265bf7f2 (ws) -q option imply info log level, not warning log level 2020-08-11 15:44:06 -07:00
Benjamin Sergeant
e7c4f0b171 add documentation for the websocket send callback and the send return type (fix #239) 2020-08-11 11:24:00 -07:00
Benjamin Sergeant
12f36b61ff (websocket server) Handle programmer error when the server callback is not registered properly (fix #227) 2020-08-06 04:40:32 -07:00
Benjamin Sergeant
b15c4189f5 add csharp/dotnet devnull client to measure througput with different runtimes 2020-08-05 13:59:26 -07:00
Benjamin Sergeant
74d3278258 add python test file to benchmark how many messages can be received per second 2020-08-04 10:53:35 -07:00
Benjamin Sergeant
831152b906 add a devnull like sample code using libwebsockets C library, to see how many messages per second a client library can receive (answer is about the same as IXWebSocket) 2020-08-02 19:26:19 -07:00
Benjamin Sergeant
7c81a98632 Add a node.js benchmarking test program, to see how fast node can receive messages. 2020-08-02 14:21:11 -07:00
Benjamin Sergeant
6e47c62c06 (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). 2020-08-02 12:41:34 -07:00
Benjamin Sergeant
bcae7f326d (ws) Add a new ws sub-command, echo_client. This command send 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) 2020-08-02 12:09:13 -07:00
Benjamin Sergeant
d719c41e31 (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) 2020-08-02 11:53:21 -07:00
Benjamin Sergeant
6f0307fb35 (build) make using zlib optional, with the caveat that some http and websocket features are not available when zlib is absent 2020-07-31 22:54:57 -07:00
Benjamin Sergeant
2e3d625c1e (websocket client) onProgressCallback not called for short messages on a websocket (fix #233) 2020-07-29 17:47:33 -07:00
Benjamin Sergeant
029289413c ws test shell script / add option so tune how large sent file will be 2020-07-29 17:46:37 -07:00
Benjamin Sergeant
4d51098c86 (websocket client) heartbeat is not sent at the requested frequency (fix #232) 2020-07-29 11:24:42 -07:00
Benjamin Sergeant
c2b05af022 can compile on macOS against jsoncpp installed from homebrew 2020-07-28 22:00:29 -07:00
Benjamin Sergeant
e85f975ab0 compiler warning fixes 2020-07-28 21:46:26 -07:00
Benjamin Sergeant
dc77d62a5d (ixcobra) CobraConnection: unsubscribe from all subscriptions when disconnecting 2020-07-28 10:32:18 -07:00
Benjamin Sergeant
4f41f209a2 (socket utility) move ix::getFreePort to ixwebsocket library 2020-07-27 18:17:13 -07:00
Benjamin Sergeant
5940e53d77 enable cobra tests which were disabled 2020-07-27 17:39:53 -07:00
Benjamin Sergeant
22dffd5b7e WebSocket::close is re-entrant 2020-07-27 17:38:33 -07:00
Benjamin Sergeant
af2f31045d snake server / join subscription background thread in the ConnectionState destructor + attach cobra message subscription id to the connection state instead of having it be a local reference that gets unbound 2020-07-27 17:35:03 -07:00
Benjamin Sergeant
5daa59f9f3 minor makefile tweaks 2020-07-27 17:19:05 -07:00
Benjamin Sergeant
2ea9d06a93 fix typo in unittest string description: ununexpected -> unsubscribed 2020-07-27 17:16:53 -07:00
Benjamin Sergeant
847fc142d1 (ixwebsocket server) change legacy api with 2 nested callbacks, so that the first api takes a weak_ptr<WebSocket> as its first argument 2020-07-25 11:42:07 -07:00
Benjamin Sergeant
0388459bd0 (ixwebsocket) add WebSocketProxyServer, from ws. Still need to make the interface better. 2020-07-25 11:26:06 -07:00
Benjamin Sergeant
9a47ec1217 (ixsnake) uses an std::thread to handle redis subscriptions (2 unittest still failing) 2020-07-24 18:12:07 -07:00
Benjamin Sergeant
45a40c8640 new Dockerfile to run locally the test on an Ubuntu 20.04 system 2020-07-24 14:46:09 -07:00
Benjamin Sergeant
e34f1c30d6 (ws) port broadcast_server sub-command to the new server API 2020-07-24 14:35:07 -07:00
Benjamin Sergeant
c14a4c0e3e formatting 2020-07-24 13:04:14 -07:00
Benjamin Sergeant
b146e93a3a (unittest) port most unittests to the new server API 2020-07-24 12:49:36 -07:00
Benjamin Sergeant
9957ec9724 (ws) port ws snake to the new server API 2020-07-24 12:33:17 -07:00
Benjamin Sergeant
78a42f61bd add tool to ease making commits 2020-07-24 11:53:09 -07:00
Benjamin Sergeant
e78019dad6 (ws) port ws transfer to the new server API 2020-07-24 11:52:16 -07:00
Benjamin Sergeant
0f026c5da2 (websocket client) reset WebSocketTransport onClose callback in the WebSocket destructor 2020-07-24 10:03:29 -07:00
Benjamin Sergeant
c26a2d5d39 (websocket server) reset client websocket callback when the connection is closed 2020-07-24 09:41:02 -07:00
Benjamin Sergeant
2798886c0b (websocket server) add a new simpler API to handle client connections / that API does not trigger a memory leak while the previous one did 2020-07-23 19:29:41 -07:00
Benjamin Sergeant
ffde283a4b (build) merge platform specific files which were used to have different implementations for setting a thread name into a single file, to make it easier to include every source files and build the ixwebsocket library (fix #226) 2020-07-17 11:58:06 -07:00
Benjamin Sergeant
f7031d0d3e set thread name in only one file 2020-07-17 11:43:50 -07:00
Benjamin Sergeant
595e6c57df IXSelectInterruptPipe.h included in cmake on windows but compiled out 2020-07-17 11:33:02 -07:00
Benjamin Sergeant
87709c201e (socket server) bump default max connection count from 32 to 128 2020-07-10 17:11:11 -07:00
Benjamin Sergeant
e70d83ace1 (snake) implement super simple stream sql expression support in snake server 2020-07-10 16:10:59 -07:00
Benjamin Sergeant
ca829a3a98 implement very very simple stream sql support 2020-07-10 16:07:51 -07:00
Benjamin Sergeant
26a1e63626 snake: stream sql mock + add republished channel option 2020-07-10 15:06:55 -07:00
Benjamin Sergeant
c98959b895 comment out unittest which cannot be activated yet 2020-07-09 10:34:52 -07:00
marcelkauf
baf18648e9
Added test for websocket leak (#225)
* Added test for websocket leak

* Fixed test
2020-07-09 10:19:44 -07:00
Benjamin Sergeant
b21306376b uwp build fix + more ivp6 support 2020-07-08 12:38:55 -07:00
Benjamin Sergeant
fbd17685a1 (socket+websocket+http+redis+snake servers) expose the remote ip and remote port when a new connection is made (see #222) / only ipv4 is handled 2020-07-08 12:10:35 -07:00
Benjamin Sergeant
3a673575dd clang format 2020-07-08 10:39:46 -07:00
Benjamin Sergeant
d5e51840ab use const iterators 2020-07-08 10:34:14 -07:00
Benjamin Sergeant
543c2086b2 more templates in WebSocketTransport 2020-07-07 21:26:42 -07:00
Benjamin Sergeant
95eab59c08 WebSocketPerMessageDeflateCompressor can work with vector or std::string 2020-07-07 21:26:04 -07:00
Benjamin Sergeant
e9e768a288 better unittest for IXWebSocketPerMessageDeflateCompressor 2020-07-07 21:15:34 -07:00
Benjamin Sergeant
e2180a1f31 add unittest for IXWebSocketPerMessageDeflateCompressor 2020-07-07 20:56:38 -07:00
Benjamin Sergeant
7c1b57c8cd (cmake) change the way zlib and openssl are searched 2020-07-07 10:58:20 -07:00
Benjamin Sergeant
89e7a35a81 add cmake comment about using a custom zlib 2020-07-06 18:34:14 -07:00
Benjamin Sergeant
de6acfe54e Merge branch 'flagarde-patch-1' 2020-07-06 18:31:05 -07:00
flagarde
789e620451 Update CMakeLists.txt
just have to provide OPENSSL_ROOT_DIR to cmake
2020-07-06 18:30:44 -07:00
Benjamin Sergeant
4789e190a0 zlib needs to be found (with vcpkg in CI), do not use our bundled copy 2020-07-06 18:09:45 -07:00
Benjamin Sergeant
d6366587a0 (UWP CI) install zlib from vcpkg 2020-07-06 17:52:28 -07:00
Benjamin Sergeant
dddf00e3b1 (cobra python bots) remove the test which stop the bot when events do not follow cobra metrics system schema with an id and a device entry 2020-07-06 16:15:24 -07:00
Benjamin Sergeant
cc47fb1c83 (cobra bots) remove bots which is not required now that we can use Python extensions 2020-06-26 16:49:08 -07:00
Benjamin Sergeant
8e8cea1bcd delete C++ code for first memory warning 2020-06-25 11:22:21 -07:00
Benjamin Sergeant
68c97da518 (cmake) new python code is optional and enabled at cmake time with -DUSE_PYTHON=1 2020-06-25 10:05:02 -07:00
Benjamin Sergeant
f8b8799799 fix windows compile error with misplaced #ifdefs/#endifs 2020-06-24 23:26:14 -07:00
Benjamin Sergeant
615f1778c3 new cobra to python bot (still sending to statsd)
values + string building can be done in python (we are embedding it)
2020-06-24 23:21:19 -07:00
Benjamin Sergeant
c45b197c85 (cobra metrics to statsd bot) fps slow frame info : do not include os name 2020-06-19 18:10:49 -07:00
Benjamin Sergeant
78713895dd (cobra metrics to statsd bot) send info about memory warnings 2020-06-19 17:46:59 -07:00
Benjamin Sergeant
aae2402ed2 disable flaky unittest once again ... ping pong is not reliable timing wise 2020-06-19 01:16:07 -07:00
Benjamin Sergeant
b62de6e516 tweak ping/pong test to be more lenient 2020-06-19 01:11:05 -07:00
Benjamin Sergeant
6e747849d7 enable ping unittest, which is flaky -> see #218 2020-06-19 01:04:44 -07:00
Benjamin Sergeant
a3a73ce1ac add unittest to test http redirection fully 2020-06-19 00:22:39 -07:00
Benjamin Sergeant
10c014bf98 (http client) fix deadlock when following redirects 2020-06-19 00:11:06 -07:00
Benjamin Sergeant
9bb3643fc7 (cobra metrics to statsd bot) send info about net requests 2020-06-18 11:25:48 -07:00
Benjamin Sergeant
56db55caca (bots) display received/sent message logs only if we are authenticated 2020-06-18 10:49:36 -07:00
Benjamin Sergeant
565a08b229 (cobra client and bots) add batch_size subscription option for retrieving multiple messages at once 2020-06-17 17:13:45 -07:00
Benjamin Sergeant
bf0f11fd65 fix size_t with int comparison warning 2020-06-17 16:56:06 -07:00
Benjamin Sergeant
558daf8911 (websocket) WebSocketServer is not a final class, so that users can extend it (fix #215) 2020-06-15 18:29:42 -07:00
Benjamin Sergeant
7ba7ff4b2a (cobra bots) minor aesthetic change, in how we display http headers with a : then space as key value separator instead of :: with
no space
2020-06-15 16:09:31 -07:00
tostc
c9854be1c4
- Updated libDiscordBot description (#214) 2020-06-14 09:58:43 -07:00
Xu Zhipei
2fbb1a846f
feat(cmake): add cmake find config support (#213) 2020-06-13 21:04:43 -07:00
Benjamin Sergeant
aa142df486 (cobra metrics to statsd bot) change from a statsd type of gauge to a timing one 2020-06-12 13:47:01 -07:00
Benjamin Sergeant
5e200a440f (redis cobra bots) capture most used devices in a zset 2020-06-11 18:49:45 -07:00
Benjamin Sergeant
6ed8723d7d try to fix a double linking error on Windows with linenoise.hpp 2020-06-11 18:01:45 -07:00
Benjamin Sergeant
ac9710d5d6 (ws) add bare bone redis-cli like sub-command, with command line editing powered by libnoise 2020-06-11 17:30:42 -07:00
Benjamin Sergeant
35d76c20dc add redis cli skeleton ws sub-command 2020-06-11 13:51:10 -07:00
Benjamin Sergeant
ca7344d9dc use reference in range for loop 2020-06-11 13:50:37 -07:00
Benjamin Sergeant
7603d1a71b (redis cobra bots) ws cobra metrics to redis / hostname invalid parsing 2020-06-11 08:33:36 -07:00
Benjamin Sergeant
d0cd4aed5a (redis cobra bots) xadd with maxlen + fix bug in xadd client implementation and ws cobra metrics to redis command argument parsing 2020-06-11 08:20:03 -07:00
Benjamin Sergeant
c5aadffa08 (redis cobra bots) update the cobra to redis bot to use the bot framework, and change it to report fps metrics into redis streams. 2020-06-10 22:30:55 -07:00
Benjamin Sergeant
ecfca1f905 cobra bots: handle stalled connection by disconnecting and reconnecting instead of quitting, and expecting kubernete to restart us 2020-06-09 21:39:37 -07:00
Benjamin Sergeant
e49bf24d2d add device counter 2020-06-09 20:10:37 -07:00
Benjamin Sergeant
2a1cd6bb3e Merge branch 'feature/cobra_metrics_to_statsd' 2020-06-08 18:34:18 -07:00
Benjamin Sergeant
766e33774c count slow frames 2020-06-08 18:19:01 -07:00
Benjamin Sergeant
9b90b1d302 fix cmake warning about mbedtls name incorrect case (all uppercase) 2020-06-04 15:01:49 -07:00
Benjamin Sergeant
ee8a3a52ec compile tweak on linux + version bump 2020-06-04 15:01:16 -07:00
Benjamin Sergeant
531bd624b5 remove spaces in keys + verbose statsd 2020-06-04 14:57:36 -07:00
Benjamin Sergeant
abd6581242 (cobra bots) set thread name for utility threads 2020-06-04 09:52:44 -07:00
Benjamin Sergeant
7095367b93 (cobra bots) set thread name for utility threads 2020-06-04 09:52:35 -07:00
Benjamin Sergeant
3bb359a774 fix warnings 2020-06-04 09:48:16 -07:00
Benjamin Sergeant
1c6ff733f9 fix cmake warning about mbedtls name incorrect case (all uppercase) 2020-06-04 09:41:12 -07:00
Benjamin Sergeant
2ecf5d8a5a (statsd cobra bots) statsd improvement: prefix does not need a dot as a suffix, message size can be larger than 256 bytes, error handling was invalid, use core logger for logging instead of std::cerr 2020-06-04 09:37:16 -07:00
Benjamin Sergeant
0f88969b77 add metrics statsd files 2020-06-04 09:36:28 -07:00
Benjamin Sergeant
c317100b47 (statsd cobra bots) statsd improvement: prefix does not need a dot as a suffix, message size can be larger than 256 bytes, error handling was invalid, use core logger for logging instead of std::cerr 2020-06-04 09:35:55 -07:00
Benjamin Sergeant
b029f176b6 add new subcommand + skeleton files 2020-06-03 16:29:25 -07:00
Benjamin Sergeant
bcfcfb628e build docker file with alpine 3.12 2020-06-02 20:31:32 -07:00
Benjamin Sergeant
268f528423 httpd gzip compression / set a response header and search for gzip in the request header in case deflate is added to gzip in there 2020-06-01 17:11:42 -07:00
Benjamin Sergeant
31be2e2527 update .gitignore file 2020-06-01 17:01:59 -07:00
Benjamin Sergeant
502f021a0e embedded help for httpd tool 2020-06-01 17:01:12 -07:00
Benjamin Sergeant
b0b451d2c7 add a simple httpd server standalone example 2020-05-29 22:10:23 -07:00
Benjamin Sergeant
4872b59fac fix windows build (or operator is not supported || is required 2020-05-29 17:18:09 -07:00
Benjamin Sergeant
bb1be240ec fix linux compile failure 2020-05-29 16:53:57 -07:00
Benjamin Sergeant
b008c97c83 (http server) support gzip compression 2020-05-29 16:49:29 -07:00
Benjamin Sergeant
9886a30490 fix #210 / better standalone example, an echo client 2020-05-27 16:24:33 -07:00
Benjamin Sergeant
4ed5206d79 fix #210 / add include to README hello world example 2020-05-27 16:04:39 -07:00
Benjamin Sergeant
33916869f1 add simple doc link for multipart uploads - fix #209 2020-05-27 10:38:32 -07:00
Benjamin Sergeant
9ddf707804 add script to build with Android NDK 2020-05-26 15:15:45 -07:00
Benjamin Sergeant
3a020a66b7 Merge branch 'feature/badges' 2020-05-21 09:54:41 -07:00
Benjamin Sergeant
bd39e69185
Update index.md 2020-05-21 09:42:15 -07:00
Benjamin Sergeant
9d4ca3f34e
Update README.md 2020-05-21 09:35:33 -07:00
Benjamin Sergeant
de6f3ded09 ci / break unittest job into small job files 2020-05-21 09:01:50 -07:00
flagarde
e0aace33ea
Update CMakeLists.txt (#207) 2020-05-21 08:46:06 -07:00
Benjamin Sergeant
16eb269e1e bump version for (compiler fix) support clang 5 and earlier (contributed by @LunarWatcher) #206 2020-05-20 10:58:30 -07:00
Benjamin Sergeant
2319dec278 (cmake) revert CMake changes to fix #203 and be able to use an external OpenSSL 2020-05-20 10:56:58 -07:00
Olivia Zoe
f1be48aff1
Re-enable support for clang 5 and earlier (#206) 2020-05-20 10:56:24 -07:00
Liz3
93fd44813a
extend docs (#204) 2020-05-18 09:22:25 -07:00
Benjamin Sergeant
54d4d81bf4 (cmake) make install cmake files optional to not conflict with vcpkg
See https://github.com/microsoft/vcpkg/pull/11030
2020-05-17 20:36:46 -07:00
Benjamin Sergeant
ea207d8199 (windows + tls) mbedtls is the default windows tls backend + add ability to load system certificates with mbdetls on windows 2020-05-17 20:36:46 -07:00
Liz3
e8287e91e4
Updated project reference/description (#202)
* edit project reference

* simple rephrase
2020-05-15 09:48:28 -07:00
Benjamin Sergeant
c0505ac7fb windows build fix with max which is a macro 2020-05-12 21:48:41 -07:00
Benjamin Sergeant
1af39bf0eb (ixbots) add options to limit how many messages per minute should be processed 2020-05-12 21:40:17 -07:00
Benjamin Sergeant
2e904801a0 (ixbots) add new class to configure a bot to simplify passing options around 2020-05-12 19:08:16 -07:00
Benjamin Sergeant
cc72494b63 Add reference to DisCPP to the README (fix #198) 2020-05-09 21:08:34 -07:00
Benjamin Sergeant
fa9a4660c6 bump some test timeout 2020-05-08 10:03:18 -07:00
Benjamin Sergeant
4773af8f2f (openssl tls) (openssl < 1.1) logic inversion - crypto locking callback are not registered properly 2020-05-08 09:54:42 -07:00
Benjamin Sergeant
c1403df74a (cmake) default TLS back to mbedtls on Windows Universal Platform 2020-05-08 09:31:53 -07:00
Benjamin Sergeant
3912e22b28 give websocket_subprotocol test more time to establish a connection 2020-05-08 09:26:05 -07:00
XLPhere
c9d5b4a581
Moved fPIC option to the top of the CMakeLists (#197)
The fPIC option was not properly registered before
2020-05-08 08:00:51 -07:00
Benjamin Sergeant
9f8643032d fix dumb compile error 2020-05-06 22:07:47 -07:00
Benjamin Sergeant
0772ef7ef5 (cobra bots) add a --heartbeat_timeout option to specify when the bot should terminate because no events are received 2020-05-06 22:01:48 -07:00
Benjamin Sergeant
c030a62c8b openSSLLockingCallback should be static 2020-05-06 16:57:53 -07:00
Benjamin Sergeant
931530b101 only register the crypto lock callback if no-one has registered them before us 2020-05-06 16:49:04 -07:00
Benjamin Sergeant
6c205b983e (openssl tls) when OpenSSL is older than 1.1, register the crypto locking callback to be thread safe. Should fix lots of CI failures 2020-05-06 16:26:30 -07:00
Benjamin Sergeant
a65b334961 assert that the timeout is non zero in makeCancellationRequestWithTimeout 2020-05-06 15:53:27 -07:00
Benjamin Sergeant
2de8aafcbc another windows build error in IXUdpSocket ... 2020-05-05 08:29:39 -07:00
Benjamin Sergeant
f075f586e1 fix windows compile error with UdpSocket::recvfrom 2020-05-05 08:15:01 -07:00
Benjamin Sergeant
93cb898989 fix compile error with UdpSocket::recvfrom 2020-05-05 08:03:04 -07:00
Benjamin Sergeant
e4da62547b add reference to multiple projects using IXWebSocket 2020-05-05 07:52:02 -07:00
Benjamin Sergeant
2b4c06e6d2 UdpSocket::recvfrom last argument does not have to be a uint32_t 2020-05-05 07:49:07 -07:00
tostc
7337ed34a6
Added asynchronous udp receive function (#193)
* Added asynchronous udp receive function

* Remove receive_async and added low level recv, which is non-blocking.

* Remove thread include

* Moved unix include to IXNetSystem.h
2020-05-05 07:47:41 -07:00
Benjamin Sergeant
15355188d5 (http client) rework a bit PATCH pull request, fix compile error with setForceBody and initialize _forceBody to false 2020-05-05 07:43:55 -07:00
Liz3
8760c87635
add PATCH and add option to enforce a http request body write (#195)
* add PATCH and add option to enforce a http request body write

* remove private bool prop
2020-05-05 07:38:55 -07:00
Benjamin Sergeant
2786631e3b clang-format 2020-05-04 17:19:25 -07:00
Benjamin Sergeant
1b30061a4d remove unused variable 2020-05-04 17:18:21 -07:00
Benjamin Sergeant
af003fc79b (ixbots) fix tsan data race error when accessing verbose parameter 2020-05-04 17:15:35 -07:00
Benjamin Sergeant
4f17cd5e74 (cobra bots) do not use a queue to store messages pending processing, let the bot handle queuing 2020-05-04 15:45:11 -07:00
Benjamin Sergeant
b04764489c (doc) add link to a project using ixwebsocket #187 2020-05-04 09:21:39 -07:00
Benjamin Sergeant
fc4a4bfb7c
fix #194 / linux needs to built with position independant code 2020-05-03 12:19:58 -07:00
ebenali
9e54fd5f1a
Fix CMake/zlibstatic-related regression (#192)
* cmake: add export() and install(EXPORT) for easier packageability

Enable the package to be more readily packageable as a system-wide
install or as a third-party dependency to another CMake-base project

This does not change CMake version requirements AFAICT

* CMake: link-in OpenSSL::Crypto

* CMake: explicitly manage dependencies. Fixes building with zlibstatic
2020-05-02 22:08:58 -07:00
ebenali
1096f62196
cmake: add export() and install(EXPORT) for easier packageability (#190)
* cmake: add export() and install(EXPORT) for easier packageability

Enable the package to be more readily packageable as a system-wide
install or as a third-party dependency to another CMake-base project

This does not change CMake version requirements AFAICT

* CMake: link-in OpenSSL::Crypto
2020-05-02 20:20:59 -07:00
Benjamin Sergeant
b34d9f6a06 uwp fixes 2020-05-01 11:27:59 -07:00
Benjamin Sergeant
b21e2506bf (ci) add universal windows platform 2020-05-01 10:44:10 -07:00
Benjamin Sergeant
303f99a432 refine previous commit 2020-05-01 10:37:25 -07:00
Benjamin Sergeant
a42ccea8dd disable unicode 2020-05-01 09:22:03 -07:00
Benjamin Sergeant
beb26bc096 use ninja for local builds 2020-04-29 11:53:56 -07:00
Benjamin Sergeant
b45980f0f6 (http client) better current request cancellation support when the HttpClient destructor is invoked (see #189) 2020-04-29 11:53:23 -07:00
Benjamin Sergeant
fbca513008 bump version 2020-04-27 12:36:56 -07:00
Benjamin Sergeant
33ebd00932 fix cmake tls backend option parsing 2020-04-27 11:29:50 -07:00
Benjamin Sergeant
fbe5e74109 fix openssl cmake errors 2020-04-27 10:59:47 -07:00
Benjamin Sergeant
a9f5d5353f fix cmake syntax error and convert some errors to fatal errors 2020-04-27 10:29:27 -07:00
Benjamin Sergeant
22e0083832 CMake TLS cleanup 2020-04-27 10:09:51 -07:00
Benjamin Sergeant
5632360fbd (http client) Set default values for most HttpRequestArgs struct members (fix #185) 2020-04-27 09:43:31 -07:00
Benjamin Sergeant
20294841b3 ci - on windows, disable building tls as it is too slow (> 15minutes total) 2020-04-25 15:58:56 -07:00
Benjamin Sergeant
74efdfebba remove bundled mbedtls 2020-04-25 15:41:39 -07:00
Benjamin Sergeant
0ab04f51fe (ssl) Default to OpenSSL on Windows, since it can load the system certificates by default 2020-04-25 15:36:31 -07:00
Benjamin Sergeant
4ed7968b05 ci / try to force an openssl 1.1 install on mac 2020-04-25 11:53:09 -07:00
Benjamin Sergeant
287e48962f bump version number 2020-04-25 11:41:58 -07:00
Francisco Javier
953c680eee
Bug on setting extra headers. Now it loses the first string character. (#184)
Client code:

    ...

    ix::WebSocketHttpHeaders headers {
        {"Cookie", "ABC"}
    };

    ...

Expected header string on server:
    "Cookie: ABC"

Resulted header string on server:
    "Cookie: BC"

Solution:
    The easy way I found to solve the problem is to add a space where extra headers are set before sended to server.

Co-authored-by: Fco. Javier M. C <fcojavmc@todo-redes.com>
2020-04-25 11:39:37 -07:00
Benjamin Sergeant
2802cad8c4 more tls in memory certs doc + bump file format 2020-04-24 15:50:39 -07:00
Benjamin Sergeant
9f770b10c0 clang-format 2020-04-24 15:34:00 -07:00
Max Weisel
677f79b0ea
Implement API for adding custom roots via a string (#178)
* Implement API for adding custom roots via a string. SocketTLSOptions API design needs work, but the IXSocketOpenSSL implementation feels good to me.

* Improve API design for specifying roots from memory.

* Add in-memory root CAs mbedtls implementation.

* Fix bug in newer versions of OpenSSL with in-memory certificate handling.
2020-04-24 15:32:11 -07:00
Benjamin Sergeant
646b18bf28 core logger support multiple level + switch ixbots to user corelogger instead of spdlog 2020-04-24 15:17:50 -07:00
Benjamin Sergeant
0670954faf unittest / remove deleted file reference 2020-04-24 14:23:38 -07:00
Benjamin Sergeant
2469d7102e missing headers for url parsing 2020-04-24 14:13:15 -07:00
Benjamin Sergeant
79acb915ce merge the 2 url parsing file into one, fix a silly build error 2020-04-24 14:08:59 -07:00
Benjamin Sergeant
bad3adb6b4 cmake pb with renamed file 2020-04-24 12:55:00 -07:00
Benjamin Sergeant
e3dd4e60c0 remove deleted IXSelectInterruptEventFd file reference in cmake 2020-04-24 12:52:13 -07:00
Benjamin Sergeant
c70f1d09a8 include all ssl backends inside special per backend macro 2020-04-24 12:47:47 -07:00
Benjamin Sergeant
4b2b133c10 fix #182 2020-04-22 14:26:16 -07:00
Benjamin Sergeant
cd5fae6a5b generate a compilation database when building with make for the default target, so that clang-tidy can be used 2020-04-22 14:14:09 -07:00
Ross Jacobs
5860c5c80b
Fixes #179 (#180) 2020-04-20 22:59:20 -07:00
Benjamin Sergeant
36257cbfe4 update build doc 2020-04-18 03:49:26 -07:00
Benjamin Sergeant
68ee57a6a7 fix ixbots unittest 2020-04-17 10:09:52 -07:00
Benjamin Sergeant
9d79596629 (ixbots) display sent/receive message, per seconds as accumulated 2020-04-17 09:56:09 -07:00
Benjamin Sergeant
0b6fd989f5 (ws) add a --logfile option to configure all logs to go to a file 2020-04-17 09:35:47 -07:00
Benjamin Sergeant
1c19a57fef missing atomic header in IXCobraBot.h / should fix windows build 2020-04-16 22:54:43 -07:00
Benjamin Sergeant
a2abe861d3 (cobra bots) add a utility class to factor out the common bots features (heartbeat) and move all bots to used it + convert cobra_subscribe to be a bot and add a unittest for it 2020-04-16 21:58:10 -07:00
Benjamin Sergeant
0f5d15aa11 (cobra bots) add a utility class to factor out the common bots features (heartbeat) and move cobra to sentry bot to use it 2020-04-16 14:49:49 -07:00
Benjamin Sergeant
ccfd196863 clang-format 2020-04-16 11:58:06 -07:00
Benjamin Sergeant
9b8cfa0a37 (websocket) add a positive number to the heartbeat message sent, incremented each time the heartbeat is sent 2020-04-15 18:33:36 -07:00
Benjamin Sergeant
85f6b1e0b7 fix compiler warning in ixsentry about unused parameters in uploadPayload method 2020-04-15 18:05:00 -07:00
Benjamin Sergeant
64754df66c (ixcobra) change cobra event callback to use a struct instead of several objects, which is more flexible/extensible 2020-04-15 17:38:39 -07:00
Benjamin Sergeant
71a421eefc remove file that does not exist yet but which is referenced in CMake 2020-04-15 17:03:34 -07:00
Benjamin Sergeant
386ef3ab04 (ixcobra) make CobraConnection_EventType an enum class (CobraEventType) 2020-04-15 16:59:17 -07:00
Benjamin Sergeant
2c4bf8f4bd (ixsentry) add a library method to upload a payload directly to sentry 2020-04-14 22:02:51 -07:00
Benjamin Sergeant
3a2c446225 missing headers in IXWebSocketCloseInfo.h, ,IXWebSocketErrorInfo.h and IXWebSocketOpenInfo.h 2020-04-14 21:52:27 -07:00
Benjamin Sergeant
35630fe7ed new makefile target + better error description in Socket::readBytes 2020-04-14 21:50:56 -07:00
Benjamin Sergeant
bea582c208 cobra subscriber in fluentd mode insert a created_at timestamp entry 2020-04-14 15:30:30 -07:00
Benjamin Sergeant
783d1d92dd snake server / handle invalid incoming json messages 2020-04-14 15:12:35 -07:00
Benjamin Sergeant
415f6b4832 (unittest) remove cmake reference to deleted file 2020-04-13 22:07:18 -07:00
Benjamin Sergeant
13d3300a40 fix unittest / simple build thing 2020-04-13 22:00:48 -07:00
Benjamin Sergeant
432f0570f4 (websocket) WebSocketMessagePtr is a unique_ptr instead of a shared_ptr 2020-04-13 21:56:01 -07:00
Benjamin Sergeant
37a054723a (websocket) use persistent member variable as temp variables to encode/decode zlib messages in order to reduce transient allocations 2020-04-13 21:38:15 -07:00
Benjamin Sergeant
c57cf413fb (ws) add a --runtime option to ws cobra_subscribe to optionally limit how much time it will run 2020-04-13 19:03:53 -07:00
Benjamin Sergeant
f1c106728b (third_party deps) fix #177, update bundled spdlog to 1.6.0 2020-04-11 13:32:16 -07:00
Benjamin Sergeant
2eb5c9480e
Create stale.yml 2020-04-06 11:20:01 -07:00
Benjamin Sergeant
f9d75c9374 (windows) when using OpenSSL, the system store is used to populate the cacert. No need to ship a cacert.pem file with your app. 2020-04-04 18:33:01 -07:00
Benjamin Sergeant
d1cd5e62ac update doc 2020-04-04 17:54:15 -07:00
Benjamin Sergeant
f3b97097cd (windows) ci: windows build with TLS (mbedtls) + verify that we can be build with OpenSSL 2020-04-04 17:49:52 -07:00
Benjamin Sergeant
605be72579 use default mkdocs theme 2020-04-04 11:05:14 -07:00
Benjamin Sergeant
49ff3789b5 mkdocs / use codehilite engine for syntax highlighting 2020-03-31 23:18:47 -07:00
Benjamin Sergeant
96d61c6e5b doc - add code block highlighting 2020-03-31 20:56:51 -07:00
Benjamin Sergeant
9a23c5aaac (doc) use c++ instead of cpp to mark a block of C++ code 2020-03-31 20:29:40 -07:00
Benjamin Sergeant
d81e4d4fc0 setHeartBeatPeriod -> setPingInterval (in doc + disabled unittests) 2020-03-31 18:36:50 -07:00
Benjamin Sergeant
bd44d32fdb try the material theme for the documentation 2020-03-31 18:32:48 -07:00
Benjamin Sergeant
b6abc12ecd Add documentation about how to make a pull request to get the latest version of the package in vcpkg (#173) 2020-03-31 15:58:01 -07:00
Benjamin Sergeant
2268b743ae add broadcasting test where 10 clients exchange messages, to try to trigger threading errors 2020-03-30 22:27:41 -07:00
Benjamin Sergeant
1d3db5f75b (cobra to statsd bot) add ability to extract a numerical value and send a timer event to statsd, with the --timer option 2020-03-30 16:08:47 -07:00
Benjamin Sergeant
296762ce06 add a docker deploy makefile target to build docker and push the built container in one shot 2020-03-29 22:08:36 -07:00
Benjamin Sergeant
e465f7af52 (cobra to statsd bot) bot init was missing + capture socket error 2020-03-29 22:03:27 -07:00
Benjamin Sergeant
f8bf1fe7cd (cobra to statsd bot) add ability to extract a numerical value and send a gauge event to statsd 2020-03-29 19:32:43 -07:00
Benjamin Sergeant
cfa5718e40 (ws cobra subscriber) use a Json::StreamWriter to write to std::cout, and save one std::string allocation for each message printed 2020-03-29 15:24:46 -07:00
Benjamin Sergeant
40c619c1ec (docker) trim down docker image (300M -> 12M) / binary built without symbol and size optimization, and source code not copied over 2020-03-29 13:06:44 -07:00
Benjamin Sergeant
22b02e0e5c update doc 2020-03-28 10:46:42 -07:00
Benjamin Sergeant
738a3bf1c5 update bundled jsoncpp to 1.9.3
(still comment the deprecation warning, which we should eventually fix ...)
2020-03-28 10:44:05 -07:00
Benjamin Sergeant
598fb071e3 have some make target compile in release with debug 2020-03-28 10:33:22 -07:00
Benjamin Sergeant
686aface26 bump version to 9.1.3 2020-03-28 10:33:05 -07:00
Benjamin Sergeant
3073dd3f06 alpine docker file installs ca-certificates (for TLS) 2020-03-28 10:32:25 -07:00
Benjamin Sergeant
68c64f3f69 use alpine as the docker distribution 2020-03-27 17:38:35 -07:00
Benjamin Sergeant
771ebb2a4c (mac ssl) rename DarwinSSL -> SecureTransport (see this too -> https://github.com/curl/curl/issues/3733) 2020-03-26 19:40:52 -07:00
Benjamin Sergeant
0fffb1e894 (websocket) fix data race accessing _socket object without mutex protection when calling wakeUpFromPoll in WebSocketTransport.cpp 2020-03-26 19:31:59 -07:00
Benjamin Sergeant
18164c0c38 New CI builder: Mac + MbedTLS + Thread Sanitizer 2020-03-26 19:16:04 -07:00
Benjamin Sergeant
d2db7310ff (ixcobra) add explicit event types for handshake, authentication and subscription failure, and handle those by exiting in ws_cobra_subcribe and friends 2020-03-26 18:54:28 -07:00
Benjamin Sergeant
09e4584fc8 New CI builder: Mac + OpenSSL + Thread Sanitizer 2020-03-26 16:46:47 -07:00
Matthew Albrecht
da36856d85
Only find zlib if it has not already been found. (#169) 2020-03-26 09:39:51 -07:00
Benjamin Sergeant
dffa759f71 move IXBench code under ixwebsocker folder 2020-03-24 20:53:25 -07:00
Benjamin Sergeant
61e789d6a4 formatting 2020-03-24 20:37:55 -07:00
Benjamin Sergeant
37cb2cc266 (ws connect) display statistics about how much time it takes to stop the connection / cf #168 2020-03-24 20:29:09 -07:00
Benjamin Sergeant
179e17895d unique_ptr for sockets 2020-03-24 12:48:55 -07:00
Benjamin Sergeant
9f818c7acf (socket) selectInterrupt member is an unique_ptr instead of being a shared_ptr 2020-03-24 10:00:41 -07:00
Benjamin Sergeant
9dcc2538ae (websocket) reset per-message deflate codec everytime we connect to a server/client 2020-03-23 18:46:30 -07:00
Benjamin Sergeant
f41a54186c (websocket) fix #167, a long standing issue with sending empty messages with per-message deflate extension (and hopefully other zlib bug) 2020-03-23 15:21:53 -07:00
Benjamin Sergeant
e0733d205c fix linux linker error 2020-03-22 21:59:30 -07:00
Benjamin Sergeant
f72f845ad2 trim headers and unused code in IXUdpSocket 2020-03-22 21:51:41 -07:00
Benjamin Sergeant
b7e7837d76 fix simple compile error 2020-03-22 19:43:43 -07:00
Benjamin Sergeant
fe966b19c7 re-enable unittests 2020-03-22 19:39:28 -07:00
Benjamin Sergeant
a0ffb2ba53 cobra to statsd bot ported to windows + add unittest 2020-03-22 19:37:04 -07:00
Benjamin Sergeant
5ad54a8904 pre-commit / fix simple file trailing things 2020-03-21 19:31:38 -07:00
Benjamin Sergeant
10e132e8ef remove std::cerr in IXRedisServer which triggers a tsan error 2020-03-20 17:50:08 -07:00
Benjamin Sergeant
5ce846f48b indent files 2020-03-20 17:00:18 -07:00
Benjamin Sergeant
1d6373335c (websocket+tls) fix hang in tls handshake which could lead to ANR, discovered through unittesting. 2020-03-20 16:57:27 -07:00
Benjamin Sergeant
829751b7af (cobra) CobraMetricsPublisher can be configure with an ix::CobraConfig + more unittest use SSL in server + client 2020-03-20 12:22:00 -07:00
Benjamin Sergeant
5691b55967 (unittest) / try to run the cobra 2 sentry bot test with SSL if the platform supports it 2020-03-19 18:50:46 -07:00
Benjamin Sergeant
575bceb1ec add make target for ubsan, tsan and asan, and enable running the unittest on mac with tsan 2020-03-18 20:53:54 -07:00
Benjamin Sergeant
6085839ef7 minor refactoring 2020-03-18 11:45:28 -07:00
Benjamin Sergeant
696d802703 bump version 2020-03-18 01:15:15 -07:00
Benjamin Sergeant
b287730c19 Simplify ping/pong based heartbeat implementation 2020-03-18 01:14:08 -07:00
Benjamin Sergeant
d6f534de06 (ws) ws echo_server gains a new option (-p) to disable responding to pings with pongs 2020-03-18 00:01:57 -07:00
Benjamin Sergeant
8ec515f292 (ws) ws connect gains a new option to set the interval at which to send pings 2020-03-17 23:54:32 -07:00
Benjamin Sergeant
c6204f4d90 tweak mkdoc action 2020-03-17 10:47:06 -07:00
Benjamin Sergeant
7dfad9c0cc more docs 2020-03-17 10:41:20 -07:00
Benjamin Sergeant
21fac0be6c
Create mkdocs.yaml github action from the web-ui 2020-03-17 10:22:58 -07:00
Benjamin Sergeant
0bddf5e096 remove workflow created manually 2020-03-17 10:21:51 -07:00
Benjamin Sergeant
946a8231e0 add ci to build documentation 2020-03-17 10:14:41 -07:00
Benjamin Sergeant
49d1e8493a update build badge on doc 2020-03-17 10:09:41 -07:00
Benjamin Sergeant
6198657dd6 do not trigger unittest when the docs is changed 2020-03-17 08:54:31 -07:00
Benjamin Sergeant
385d6f5f4a (doc) move tls options to its own section 2020-03-17 08:49:42 -07:00
Benjamin Sergeant
3919153a7b close #164 / add reference to tls option which must be set to true in server mode 2020-03-17 08:45:39 -07:00
Benjamin Sergeant
e8f81776f9 (cobra to sentry bot + docker) default docker file uses mbedtls + ws cobra_to_sentry pass tls options to sentryClient. 2020-03-16 10:05:21 -07:00
Benjamin Sergeant
0bb5462504
Feature/ci windows (#163)
* win only

* disable ixcrypto mbedtls search on windows

* ws cmakefile do not search for openssl

* ci builds files on top of cmaking

* ci builds files on top of cmaking / syntax tweak

* use gha-setup-vsdevenv syntax

* build fix and hacks

* try to run unittest on win

* try to run unittest on win (syntax error)

* unittest wip

* wip

* wip again

* wip again (working-directory)

* cleanup

* dumb compile error
2020-03-15 18:38:09 -07:00
Benjamin Sergeant
44f599747e (cobra client) ws cobra subscribe resubscribe at latest position after being disconnected 2020-03-13 17:30:31 -07:00
Benjamin Sergeant
9801ebdb36 (cobra client) can subscribe with a position 2020-03-13 16:06:13 -07:00
Benjamin Sergeant
332ffb0603 (cobra client) pass the message position to the subscription data callback 2020-03-13 12:49:37 -07:00
Benjamin Sergeant
90df3d1805 (openssl tls backend) Fix a hand in OpenSSL when using TLS v1.3 ... by disabling TLS v1.3 2020-03-12 16:27:25 -07:00
Benjamin Sergeant
bda1bb6ab4 expose a way to set tls options for a sentry client, for testing 2020-03-12 16:18:28 -07:00
Benjamin Sergeant
d4e1f71e3c (cobra2sentry bot) take a sentry client as input instead of a dsn 2020-03-12 12:30:58 -07:00
Benjamin Sergeant
adf6aa1d6c (cobra2sentry bot) remove the jobs option passed to ws, and only use one sentry sender 2020-03-12 12:24:25 -07:00
Benjamin Sergeant
cb1f9f5a44 clang formatting 2020-03-12 12:15:56 -07:00
Benjamin Sergeant
83ae105edb minor refactoring to delete files which are not needed 2020-03-12 12:13:31 -07:00
Benjamin Sergeant
7642ccc99e (unittest) fix silly compile error with renaming of Logger to TLogger 2020-03-12 11:15:54 -07:00
Benjamin Sergeant
cb1ec7dc96 add unittest for cobra to sentry bots 2020-03-12 09:07:01 -07:00
Benjamin Sergeant
09b9483ddf fix casing problem with cmake filename 2020-03-11 16:04:16 -07:00
Benjamin Sergeant
27a8ae309f build failure on Linux 2020-03-11 15:59:48 -07:00
Benjamin Sergeant
3df7c942d7 move sentry and statsd cobra ws commands into a new ixbots folder 2020-03-11 15:55:56 -07:00
Benjamin Sergeant
6a4d69afc5 (cobra) IXCobraConfig struct has tlsOptions and per message deflate options 2020-03-11 12:40:32 -07:00
Benjamin Sergeant
0a11132b07 (cobra) add IXCobraConfig struct to pass cobra config around 2020-03-11 10:48:41 -07:00
Benjamin Sergeant
cb9f0cb968 (doc) mention that OpenSSL can be used on Windows 2020-03-11 10:18:48 -07:00
Benjamin Sergeant
b1f30bb40f (ws cobra_subscribe) add a --fluentd option to wrap a message in an enveloppe so that fluentd can recognize it 2020-03-09 15:25:43 -07:00
Benjamin Sergeant
4ef04b8339 (websocket server) fix regression with disabling zlib extension on the server side. If a client does not support this extension the server will handle it fine. We still need to figure out how to disable the option. cc #160 2020-03-02 16:53:08 -08:00
Rok Krhlikar
e581f29b42
Move zlib include_directories before add_subdirectory (#159) 2020-02-28 09:30:37 -08:00
Benjamin Sergeant
a42f115f79 compatibility: add node.js example server 2020-02-26 12:17:34 -08:00
Benjamin Sergeant
5ce1a596cf add a python echo server that does not close the connection after each received messages 2020-02-26 12:11:31 -08:00
Benjamin Sergeant
21db7b6c5b add a simple pytho echo client 2020-02-26 11:50:24 -08:00
Benjamin Sergeant
e15a2900e7 (websocket) traffic tracker received bytes is message size while it should be wire size 2020-02-26 11:24:41 -08:00
Benjamin Sergeant
140a21c8b3 (ws_connect) display sent/received bytes statistics on exit 2020-02-26 11:23:36 -08:00
Benjamin Sergeant
6d0c568aaa update doc / fix incorrect comment about sending defaultint to binary mode 2020-02-24 16:24:32 -08:00
Benjamin Sergeant
c96abcef1c build status github badge 2020-02-23 09:46:08 -08:00
Benjamin Sergeant
4a9b0b9dfd (server) give thread name to some usual worker threads / unittest is broken !! 2020-02-23 09:44:58 -08:00
Benjamin Sergeant
8837d5e784 (websocket server) fix regression from 8.1.2, where per-deflate message compression was always disabled 2020-02-22 10:15:43 -08:00
Benjamin Sergeant
242c945400 (client + server) Fix #155 / http header parser should treat the space(s) after the : delimiter as optional. Fixing this bug made us discover that websocket sub-protocols are not properly serialiazed, but start with a , 2020-02-21 14:05:38 -08:00
Benjamin Sergeant
feab4dee0f split httpd test case into 2 test cases 2020-02-21 12:24:22 -08:00
Benjamin Sergeant
8175829b4b unittest / add extra test for checking headers 2020-02-21 12:22:37 -08:00
Benjamin Sergeant
4c66a7561e (WebSocketServer) add option to disable deflate compression, exposed with the -x option to ws echo_server 2020-02-18 21:38:28 -08:00
Benjamin Sergeant
111475e65c (ws cobra to statsd and sentry sender) exit if no messages are received for one minute, which is a sign that something goes wrong on the server side. That should be changed to be configurable in the future 2020-02-18 12:43:07 -08:00
Benjamin Sergeant
45061b0b14 (http client + sentry minidump upload) Multipart stream closing boundary is invalid + mark some options as mandatory in the command line toolsj 2020-02-13 12:25:18 -08:00
Benjamin Sergeant
1bb847a51c bump version for #154 fix which was about removing the unused subtree which was causing some way of installing to break 2020-02-12 11:21:46 -08:00
Benjamin Sergeant
ce9feeafdf try to delete subtree usocket again 2020-02-12 10:08:42 -08:00
Benjamin Sergeant
415f0b3e6d CI/CD no need to run redis anymore, as we have our fake limited one 2020-02-07 17:15:16 -08:00
Benjamin Sergeant
94431756ff trigger a github action 2020-02-07 17:08:25 -08:00
Benjamin Sergeant
5f6c54bb90 (snake) add an option to disable answering pongs as response to pings, to test cobra client behavior with hanged connections 2020-01-31 16:55:54 -08:00
Benjamin Sergeant
f994a41845 (IXCobraConnection) set a ping timeout of 90 seconds. If no pong messages are received as responses to ping for a while, give up and close the connection 2020-01-31 16:25:45 -08:00
Benjamin Sergeant
f3760318b7 (cobra to sentry) remove noisy logging 2020-01-31 16:07:36 -08:00
Benjamin Sergeant
c2362e6875 (ixcobra) check if we are authenticated in publishNext before trying to publish a message 2020-01-30 20:52:11 -08:00
johnfoconnor
d91b24723d version 8.0.2: add severity level for log sentry events (#152)
* script to update version

* version 8.0.2: add severity level for log sentry events
2020-01-28 14:04:18 -08:00
Benjamin Sergeant
2d28b7d4ff bump version and update changelog 2020-01-28 10:50:03 -08:00
Benjamin Sergeant
86d3fc8621 quit checkConnection after sleeping if requested to stop (iterate on #151) 2020-01-28 10:46:04 -08:00
Benjamin Sergeant
422c7ff855 wait with a condition variable instead of a this_thread::sleep_for so that waiting can be cancelled when we stop/shutdown the data thread (see #151) 2020-01-28 10:04:32 -08:00
Benjamin Sergeant
1c7ccbae12 bump version 2020-01-27 17:41:27 -08:00
Benjamin Sergeant
ed2a81f115 Fix #148 2020-01-27 17:29:44 -08:00
Seth Alves
7ed8ac208a bind returns zero on success (#149) 2020-01-27 17:15:44 -08:00
Benjamin Sergeant
aa12098cb5 add -6 option to ws echo_server / cf #148 2020-01-26 16:44:44 -08:00
Benjamin Sergeant
5d4bb90703 (SocketServer) add ability to bind on an ipv6 address 2020-01-26 16:21:56 -08:00
Benjamin Sergeant
fad9f89846 (ws) add a dnslookup sub-command, to get the ip address of a remote host 2020-01-26 16:21:56 -08:00
ronen barzel
527308a049 Remove 'handleConnect() done' info message (#147) 2020-01-26 10:03:17 -08:00
Olivia Zoe
68b318ab97 Doc update for Conan (#146)
* Doc update for Conan

* Migration notice
2020-01-17 07:15:41 -08:00
Benjamin Sergeant
65bae2736d fix #144 - get rid of stubbed windows ssl schannel backend 2020-01-14 13:39:48 -08:00
Benjamin Sergeant
a923caec0b install gdb in the alpine dockerfile 2020-01-14 13:37:28 -08:00
Benjamin Sergeant
4d7332c4ee activate chat test on Windows 2020-01-13 16:44:10 -08:00
Benjamin Sergeant
4f3f1f3e4c simplify socket interface 2020-01-12 22:30:16 -08:00
Benjamin Sergeant
2a954b5b5b use curl code for large apple ssl send 2020-01-12 22:04:06 -08:00
Benjamin Sergeant
b96b3b099f test broadcast server 2020-01-12 21:32:26 -08:00
Benjamin Sergeant
bb31612ebe more doc 2020-01-12 11:16:54 -08:00
Benjamin Sergeant
d2c5ab1cc4 (openssl + mbedssl) fix #140, can send large files with ws send over ssl / still broken with apple ssl 2020-01-12 11:08:44 -08:00
Benjamin Sergeant
a01584ad9d compatibility websocket server / add non ssl echo_server which does not run into the ssl error (#140) 2020-01-12 10:34:33 -08:00
Benjamin Sergeant
9651f3823d (apple ssl) model write method after the OpenSSL one for consistency
More test code for #140.
2020-01-10 18:32:34 -08:00
Benjamin Sergeant
0544cdedeb fix broken autobahn link 2020-01-09 15:30:33 -08:00
Benjamin Sergeant
df0239ae68 poll: handle POLLNVAL 2020-01-09 13:45:58 -08:00
Benjamin Sergeant
c8bf2a0d82 formatting 2020-01-09 13:45:31 -08:00
Benjamin Sergeant
4d96804b22 ws send / detect disconnection earlier 2020-01-09 13:30:08 -08:00
Benjamin Sergeant
ce9db42c23 socket polling / handle more error case such as hanged socket 2020-01-09 12:26:57 -08:00
Benjamin Sergeant
a844dbc587 websocket closing / handle failure to flush send buffer as an error case 2020-01-09 12:17:37 -08:00
Benjamin Sergeant
28952cb0b0 ws send / check that we are connected when waiting for the send buffer to be flushed, and if so, abort 2020-01-09 12:16:12 -08:00
Benjamin Sergeant
fe29579755 refactor receiving socket code in its own method 2020-01-09 12:00:42 -08:00
Matthew Albrecht
b816f1fbda Allow custom OpenSSL lib, only include openssl/x509v3.h when used. (#143)
* Allow custom OpenSSL lib, only include openssl/x509v3.h when used.

Using fnmatch on Unix systems, PathMatchSpecA is the best WINAPI equivalent.

* Moved shlwapi into WIN32 block.
2020-01-08 13:57:57 -08:00
Benjamin Sergeant
1320e4ddaf Windows ws build fix 2020-01-07 17:37:38 -08:00
Benjamin Sergeant
f4a7277d61 using centos8 for the default Dockerfile, to get a gdb that display source code when running on OpenShift 2020-01-07 14:58:40 -08:00
Matthew Albrecht
34d7b18c85 Allow configuration of Mbed TLS use. (#142)
* Allow configuration of Mbed TLS use.

* Added option for OpenSSL support.

* Fixed elseif/else mixup from 5ebad24040cdb49277efc438d4c509a7d1a5a413
2020-01-07 14:57:47 -08:00
Matthew Albrecht
d72d516a92 Fixed build error with vcpkg installed. (#141) 2020-01-07 10:40:36 -08:00
Benjamin Sergeant
f6c482c65d (apple ssl) unify read and write ssl utility code 2020-01-06 15:19:14 -08:00
Benjamin Sergeant
dec8a2b9ab apple ssl: aesthetic change (get rid of yoda comparisons/conditionals) 2020-01-06 14:45:05 -08:00
Benjamin Sergeant
a5bc39be55 (websocket client) better error propagation when errors are detected while sending data + (ws send) detect failures to send big files, terminate in those cases and report error (troubleshooting #140) 2020-01-06 14:34:43 -08:00
Benjamin Sergeant
c62ad5f466 add reference ssl echo server using websockets and nginx reverse proxy to terminate ssl 2020-01-05 15:26:35 -08:00
Benjamin Sergeant
effa115ed2 Merge commit 'c992cb4e42cc223f67ede0e48d7ff3f4947af0c6' as 'test/compatibility/C/uWebSockets' 2020-01-04 15:41:03 -08:00
Benjamin Sergeant
c992cb4e42 Squashed 'test/compatibility/C/uWebSockets/' content from commit 03681cc
git-subtree-dir: test/compatibility/C/uWebSockets
git-subtree-split: 03681ccbe630eb4db6322557e6bfe8cda8f41526
2020-01-04 15:41:03 -08:00
Benjamin Sergeant
b9504fcd44 add another compatibility python client 2020-01-04 15:37:40 -08:00
Benjamin Sergeant
299bcd4b92 Add a simple python program to send a file (debugging #140) 2020-01-04 15:18:02 -08:00
Benjamin Sergeant
f56098dd4c (ws send) add option (-x) to disable per message deflate compression 2020-01-04 15:08:36 -08:00
Benjamin Sergeant
e0187b2d8e fix typo in ws embedded help 2020-01-04 14:37:53 -08:00
Benjamin Sergeant
31682f5f2d (ws send + receive) handle all message types (ping + pong + fragment) / investigate #140 2020-01-04 13:45:07 -08:00
Benjamin Sergeant
221087ffff Install source code when making a Docker container 2020-01-04 13:44:02 -08:00
Benjamin Sergeant
c7fc4f0f8e add user list in readme 2019-12-30 22:16:29 -08:00
Benjamin Sergeant
2eece1d11a correct version number 2019-12-30 22:14:53 -08:00
Benjamin Sergeant
5ba05212ec tag version 2019-12-30 17:17:28 -08:00
James Tyra
9e457871b4 Update IXSocketMbedTLS.cpp (#139)
fix bug with mbedtls server certificate loading.
2019-12-30 16:11:34 -08:00
James Tyra
b5481262fb Update IXSocketMbedTLS.cpp (#138)
fix bug just introduced.

mbedstl_pk_setup() gets automatically called later.
2019-12-30 15:14:50 -08:00
James Tyra
98e98f083e [2nd try] Update IXSocketMbedTLS.cpp (#137)
* Update IXSocketMbedTLS.cpp

fix initialization of mbedtls context.
without this, crashes under certain conditions.

* Update IXSocketMbedTLS.cpp

removed newline on 46
2019-12-30 14:38:25 -08:00
Benjamin Sergeant
d60777b9cc clang-format 2019-12-30 08:46:18 -08:00
Benjamin Sergeant
8b5e42fe84 (ws cobra to sentry/statsd) fix for handling null events properly for empty queues + use queue to send data to statsd 2019-12-28 17:28:05 -08:00
Benjamin Sergeant
d89d152ad7 (ws cobra to sentry) handle null events for empty queues 2019-12-28 10:16:18 -08:00
Benjamin Sergeant
47a3736b24 (ws cobra to sentry) game is picked in a fair manner, so that all games get the same share of sent events 2019-12-27 19:10:15 -08:00
Benjamin Sergeant
1cd7cf340a (ws cobra to sentry) refactor queue related code into a class 2019-12-27 18:24:45 -08:00
Benjamin Sergeant
3a25a05d9c (ws cobra to sentry) bound the queue size used to hold up cobra messages before they are sent to sentry. Default queue size is a 100 messages. Without such limit the program runs out of memory when a subscriber receive a lot of messages that cannot make it to sentry 2019-12-25 22:15:57 -08:00
Benjamin Sergeant
d2acfd5d1f (ws client) use correct compilation defines so that spdlog is not used as a header only library (reduce binary size and increase compilation speed) 2019-12-25 09:03:57 -08:00
Benjamin Sergeant
9dfcd8ea69 (ws client) all commands use spdlog instead of std::cerr or std::cout for logging 2019-12-24 21:55:34 -08:00
Benjamin Sergeant
ee65f95fe3 (cobra client) send a websocket ping every 30s to keep the connection opened 2019-12-24 17:16:41 -08:00
Benjamin Sergeant
b9cc6d7e23 (tls / apple) minor refactoring, move functions out of the anonymous namespace to become static member functions 2019-12-23 16:30:38 -08:00
Benjamin Sergeant
1e97e5e536 socket tls options: display ciphers 2019-12-23 12:25:25 -08:00
Benjamin Sergeant
440a1058b3 (doc) fix typo 2019-12-22 20:33:14 -08:00
Benjamin Sergeant
ff489515be v7.6.4 2019-12-22 20:32:10 -08:00
Benjamin Sergeant
536a502b60 (client) error handling, quote url in error case when failing to parse on 2019-12-22 20:30:29 -08:00
Benjamin Sergeant
db0ff4ecd1 (ws) ws_cobra_publish: register callbacks before connecting 2019-12-22 20:29:37 -08:00
Benjamin Sergeant
e4aed56d72 (doc) mention mbedtls in supported ssl server backend 2019-12-22 20:28:44 -08:00
Benjamin Sergeant
1427a97dd9 update gitignore file 2019-12-20 15:21:36 -08:00
Benjamin Sergeant
d7318f97e6 (tls) add a simple description of the TLS configuration routine for debugging 2019-12-20 15:18:04 -08:00
Benjamin Sergeant
d1a4cab134 (mbedtls) correct support for using own certificate and private key 2019-12-20 15:13:26 -08:00
Benjamin Sergeant
8e7d310439 (ws commands) in websocket proxy, disable automatic reconnections + in Dockerfile, use alpine 3.11 2019-12-20 09:51:21 -08:00
Benjamin Sergeant
9347664622 (cobra) Add TLS options to all cobra commands and classes. Add example to the doc. 2019-12-19 20:49:28 -08:00
Benjamin Sergeant
4e240e4992 formatting 2019-12-19 19:13:55 -08:00
Benjamin Sergeant
626e190d91 (cobra-to-sentry) capture application version from device field 2019-12-18 15:41:59 -08:00
Benjamin Sergeant
1933da7044 IXSentryClient: remove duplicated line 2019-12-18 15:29:53 -08:00
Benjamin Sergeant
494f408320 (tls) Experimental TLS server support with mbedtls (windows) + process cert tlsoption (client + server) 2019-12-18 11:51:02 -08:00
Benjamin Sergeant
299afc1425 (tls servers) Make it clear that apple ssl and mbedtls backends do not support SSL in server mode 2019-12-18 10:43:05 -08:00
Benjamin Sergeant
0679b6399f (tls options client) TLSOptions struct _validated member should be initialized to false 2019-12-17 14:10:28 -08:00
Benjamin Sergeant
862f8ea2d4 (websocket client) improve the error message when connecting to a non websocket server 2019-12-16 17:57:43 -08:00
Benjamin Sergeant
cae016564e (server) attempt at fixing #131 by using blocking writes in server mode 2019-12-12 12:17:29 -08:00
Benjamin Sergeant
e223f8fac2 (ws) cobra to sentry - created events with sentry tags based on tags present in the cobra messages 2019-12-11 17:28:11 -08:00
Benjamin Sergeant
c86fa8ad3b update spdlog 2019-12-06 22:05:12 -08:00
Benjamin Sergeant
ba4cf75e0f (mac) convert SSL errors to utf8 2019-12-06 16:45:49 -08:00
Benjamin Sergeant
9f98628709 Add script to extract the version from the header file and remove DOCKER_VERSION 2019-12-06 16:44:05 -08:00
Benjamin Sergeant
0b2d816320 (ws) cobra to sentry. Handle Error 429 Too Many Requests and politely wait before sending more data to sentry 2019-12-05 15:59:29 -08:00
Benjamin Sergeant
8b1c4ff081 Using Alpine edge distribution to live on the edge 2019-12-05 15:46:01 -08:00
Benjamin Sergeant
fde0b717d3 sentry minidump upload timeout 2019-12-05 15:19:27 -08:00
Benjamin Sergeant
228cdca250 bunch of docker compose dev changes 2019-12-05 15:18:02 -08:00
Benjamin Sergeant
18386ae66f (ws) #125 / fix build problem when jsoncpp is not installed locally 2019-12-03 17:18:16 -08:00
Benjamin Sergeant
5c5ea6dec1 (ws) #125 / cmake detects an already installed jsoncpp and will try to use this one if present 2019-12-03 16:01:46 -08:00
Olivia Zoe
2e1657167f Meta: documentation fixes (#127)
* Clarify versions, minor punctuation fix

* Copyediting, borked URL

* Fix Python comments in C++ code

* Copyediting

* Pretty code

* Copyediting, pretty code

* Typo

* Pretty code
2019-12-03 09:28:18 -08:00
Benjamin Sergeant
c91a0d2a35 (http client) use std::unordered_map instead of std::map for HttpParameters and HttpFormDataParameters class aliases 2019-12-03 09:25:00 -08:00
Benjamin Sergeant
d465511812 [#113] Mention StreamSSL as an example windows schannel implementation 2019-12-03 09:22:27 -08:00
Benjamin Sergeant
6b98694caa (client) internal IXDNSLookup class requires a valid cancellation request function callback to be passed in 2019-12-02 14:52:19 -08:00
Benjamin Sergeant
d82e05f72d (client) fix an overflow in the exponential back off code 2019-12-02 13:51:45 -08:00
Benjamin Sergeant
48622a24db ws cobra_subscribe / sleep 1s instead of 10ms in the main loop 2019-12-02 09:52:52 -08:00
Benjamin Sergeant
94a274ced4 Improve the limitation section in the doc about TLS, cf bug #113 2019-12-02 09:52:05 -08:00
Benjamin Sergeant
a7977cf1a5 ws cobra subcommands / channel is not a positional argument anymore 2019-11-28 15:17:13 -08:00
Benjamin Sergeant
3fffd2ed0b add doc about using ws to run a cobra server/publisher/subscriber 2019-11-27 09:26:45 -08:00
Benjamin Sergeant
a0d5f37402 (http client) Add support for multipart HTTP POST upload + (ixsentry) Add support for uploading a minidump to sentry 2019-11-25 21:11:11 -08:00
Benjamin Sergeant
1b11ef006a Update README.md 2019-11-23 12:44:24 -08:00
Benjamin Sergeant
155bbfa984 On Darwin SSL, add ability to skip peer verification. 2019-11-20 13:58:08 -08:00
Benjamin Sergeant
89aae8b344 bump version for 32-bit fix 2019-11-20 11:35:07 -08:00
Benjamin Sergeant
28a0ba4768 tweaks to the test python proxy code / (moved here) https://gist.github.com/bsergean/bad452fa543ec7df6b7fd496696b2cd8 2019-11-20 11:32:21 -08:00
fcojavmc
708969c126 Compile bug (#122)
* 1) IXWebSocketTransport: BUG: int type has no warranty of number of bits. It depends on compiler and architecture. In my system (64 bit) is 32 bit.
1 << 63 is bad idea in this case because the final number is 0 by overflow.
The symptom observed is that the server can't receive messages.

2) IXSocketFactory: Compilation Warning: Variable not in use.

* Better aproach suggested by Benjamin.
2019-11-20 11:12:24 -08:00
Benjamin Sergeant
cc492bf1a3 add a python websocket proxy which works on Linux, while ws proxy_server does not 2019-11-18 13:46:11 -08:00
Benjamin Sergeant
901c21e499 ws proxy_server / remote server close not forwarded to the client 2019-11-16 14:21:44 -08:00
Benjamin Sergeant
16c6f08e2d fix android build + proxy work 2019-11-16 06:51:53 -08:00
Benjamin Sergeant
6e9f27d63c bump version 2019-11-15 17:19:06 -08:00
Benjamin Sergeant
5b282ce3b4 document new proxy command 2019-11-15 17:18:32 -08:00
Benjamin Sergeant
c7b2446164 proxy works but crash when the connection is refused 2019-11-15 17:07:31 -08:00
Benjamin Sergeant
0e43c618a5 add stub code for ws proxy server 2019-11-15 14:30:20 -08:00
Benjamin Sergeant
af6100b90f fix typo 2019-11-15 14:28:30 -08:00
Benjamin Sergeant
a4cd248368 update readme / add contributing notes 2019-11-15 14:21:28 -08:00
Benjamin Sergeant
6e52723c8c update changelog 2019-11-06 23:12:45 -08:00
Benjamin Sergeant
a3ad92b9d9 update readme 2019-11-06 23:12:45 -08:00
Benjamin Sergeant
b07827790f check max frame size (#119) 2019-10-28 21:53:31 -07:00
Benjamin Sergeant
85e00a195c docker build fixes 2019-10-26 11:47:08 -07:00
Benjamin Sergeant
081dd2c4bb Add unittest to IXSentryClient to lua backtrace parsing code 2019-10-26 10:54:47 -07:00
Benjamin Sergeant
bbfa76a2c9 move sentry code around and add a stub unittest for it 2019-10-25 14:54:31 -07:00
Benjamin Sergeant
16a060131a ws cobra to sentry / simplify sent and received message statistic reporting 2019-10-25 14:34:48 -07:00
Benjamin Sergeant
6dabc68d29 remove unused quiet argument of ws cobra_metrics_to_redis command 2019-10-25 14:02:56 -07:00
Benjamin Sergeant
ac0593bfb3 Add cobra_metrics_to_redis sub-command to create streams for each cobra metric event being received. 2019-10-24 14:42:36 -07:00
Benjamin Sergeant
ac3e9eab25 Create SECURITY.md 2019-10-17 06:58:22 -07:00
Benjamin Sergeant
6de426a574 remove unused code in ws cobra_publish 2019-10-14 11:15:14 -07:00
Benjamin Sergeant
c3a619f114 Add client support for websocket subprotocol. Look for the new addSubProtocol method for details 2019-10-13 13:37:34 -07:00
Benjamin Sergeant
128545cc2b OpenSSL: add an extra cipher to the default cipher set, which let us connect to wss//echo.websocket.org 2019-10-10 09:37:27 -07:00
Benjamin Sergeant
8152898c4e add more docs about ws 2019-10-09 22:42:03 -07:00
Benjamin Sergeant
852bf452b6 Freebsd (#117)
* add file

* CMake freebsd fix
2019-10-09 17:00:32 -07:00
Benjamin Sergeant
b38e80f846 (freebsd compile fix) add some missing socket related headers 2019-10-09 15:38:40 -07:00
Benjamin Sergeant
a383ac10d9 make sure the unittest pass withouth SSL 2019-10-03 09:41:17 -07:00
Benjamin Sergeant
2c32f5c593 add a target for building wihout ssl + take Matt Boer updated script to run ws test with SSL (still broken for large payload) 2019-10-03 07:47:34 -07:00
Benjamin Sergeant
41cbee2cd2 ws: Signal handling code isn't include on Windows 2019-10-01 16:12:32 -07:00
Benjamin Sergeant
1f8944852a bump version 2019-10-01 16:01:32 -07:00
Benjamin Sergeant
95dd03b298 Add mbed tls version in user agent string + set user agent properly when enabling openssl on macOS 2019-10-01 15:58:35 -07:00
Benjamin Sergeant
a0cfaff528 Implement SSL server with OpenSSL backend / still flaky 2019-10-01 15:43:37 -07:00
Benjamin Sergeant
d6542383ed Add --tls option to pass to ws server command, to enable/disable tls 2019-10-01 13:54:46 -07:00
Benjamin Sergeant
afed387bcf Socket Factory has only one function which works for server and client code, and can do tls for both 2019-09-30 22:06:46 -07:00
Benjamin Sergeant
313949f087 SocketServer::handleConnection takes an std::shared_ptr<Socket> instead of a file descriptor 2019-09-30 21:48:55 -07:00
Benjamin Sergeant
e5c8e2e7f4 openSSLHandshake -> openSSLClientHandshake 2019-09-30 21:24:25 -07:00
Benjamin Sergeant
845bbc5208 all ws subcommands propagate tls options to servers (unimplemented) or ws or http client (implemented) (contributed by Matt DeBoer) 2019-09-30 18:21:20 -07:00
Benjamin Sergeant
7a26ff4de8 clang-format 2019-09-30 17:52:39 -07:00
Benjamin Sergeant
a1f3c40a2d ws has a --version option 2019-09-30 17:31:33 -07:00
Benjamin Sergeant
1fdbc2bc22 bump version number 2019-09-29 22:10:07 -07:00
Benjamin Sergeant
0f4def2338 fix windows compile error in include/spdlog/details/pattern_formatter-inl.h 2019-09-29 22:00:57 -07:00
Benjamin Sergeant
7939f7ad50 docs: WITH_TLS => USE_TLS 2019-09-29 21:31:13 -07:00
Matt DeBoer
8bfc3c5ea6 document basic usage 2019-09-29 21:29:28 -07:00
Benjamin Sergeant
bf46f3fe8f unittest / specify a cacert for tls client tests 2019-09-29 21:24:22 -07:00
Benjamin Sergeant
55141aa875 ws curl + http client tls option handling + ca cert processing for mbedtls 2019-09-29 21:13:11 -07:00
Benjamin Sergeant
4e4792d6dc update copyright dates and authors 2019-09-29 20:09:51 -07:00
Benjamin Sergeant
2aca019d84 openssl client: handle TLS options 2019-09-29 20:07:53 -07:00
Benjamin Sergeant
864249b62d most ws command take tls options, no-op for now (contributed by Matt DeBoer) 2019-09-29 18:29:51 -07:00
Benjamin Sergeant
d1fb34694c SocketTLSOptions: more methods (contributed by Matt DeBoer) 2019-09-29 17:35:18 -07:00
Benjamin Sergeant
d1fc31b894 ws transfer + send + receive / improved logging (contributed by Matt DeBoer) 2019-09-29 17:21:52 -07:00
Benjamin Sergeant
f6bf2531bb Add ability to use OpenSSL on apple platforms. 2019-09-29 15:34:58 -07:00
Benjamin Sergeant
681390f22f update and change how we build with spdlog 2019-09-29 11:13:24 -07:00
Benjamin Sergeant
0ee675e554 ixcobra / fix crash in CobraConnection::publishNext when the queue is empty + handle CobraConnection_PublishMode_Batch in CobraMetricsThreadedPublisher 2019-09-28 10:36:47 -07:00
Benjamin Sergeant
7e1a60e61d DNS lookup test works on windows 2019-09-27 14:34:47 -07:00
Benjamin Sergeant
4cd11fdbc7 mbedtls fixes / the unittest now pass on macOS, and hopefully will on Windows/AppVeyor as well. 2019-09-27 14:07:01 -07:00
Benjamin Sergeant
05c7a26e3a Export port 8008 for Docker + test_ws.sh is /bin/sh compatible 2019-09-26 14:36:14 -07:00
Benjamin Sergeant
6762978ddf http server unittest + refactoring 2019-09-26 09:45:59 -07:00
Benjamin Sergeant
658650cf24 http server: in redirect mode, POST request are given a 200 status code and an empty response 2019-09-26 09:27:27 -07:00
Benjamin Sergeant
8a662b35e1 Http server: add options to ws https to redirect all requests to a given url. 2019-09-26 09:10:30 -07:00
Benjamin Sergeant
3cd7c0194f Stop having ws send subcommand send a binary message in text mode, which would cause error in make ws_test shell script test 2019-09-25 15:39:43 -07:00
Benjamin Sergeant
05f29639e5 fix doc 2019-09-24 15:42:28 -07:00
Benjamin Sergeant
5c18ffdae2 speedup base64 code by reserving memory 2019-09-24 14:17:03 -07:00
Benjamin Sergeant
d3cee46e93 wrong mutex being used ... 2019-09-24 14:10:41 -07:00
Benjamin Sergeant
94c589f696 Fix 2 race conditions detected with TSan, one in CobraMetricsPublisher::push and another one in WebSocketTransport::sendData (that one was bad). 2019-09-24 11:46:54 -07:00
Benjamin Sergeant
490fbf4cb5 try to enable more tests on windows 2019-09-23 21:52:32 -07:00
Benjamin Sergeant
d46ce7eb63 fix tsan errors on macOS when running the unittest 2019-09-23 21:51:55 -07:00
Benjamin Sergeant
169e225ccd fix warning + add redis server logging 2019-09-23 21:14:20 -07:00
Benjamin Sergeant
ceb0c602c9 Add simple Redis Server which is only capable of doing publish / subscribe. New ws redis_server sub-command to use it. The server is used in the unittest, so that we can run on CI in environment where redis isn not available like github actions env. 2019-09-23 21:04:01 -07:00
Benjamin Sergeant
95722e3bbb fix linux compilation error, by ordering dependant libraries properly 2019-09-23 12:32:04 -07:00
Benjamin Sergeant
1cde26771a move snake code to its own subfolder like ixcobra, ixcrypto, etc... 2019-09-23 11:46:16 -07:00
Benjamin Sergeant
cd3c9d879c reformat everything with clang-format 2019-09-23 10:25:23 -07:00
Benjamin Sergeant
398c4fbf99 fix unittest 2019-09-22 19:40:33 -07:00
Benjamin Sergeant
e7b4a985b4 fix unittest compiler warnings 2019-09-22 19:22:48 -07:00
Benjamin Sergeant
6f76fea188 compile fixes 2019-09-22 18:52:57 -07:00
Benjamin Sergeant
f6b8e7f234 add gihub actions 2019-09-22 18:45:30 -07:00
Benjamin Sergeant
041fa3e340 compiled fixes on mac and windows 2019-09-22 18:43:57 -07:00
Matt DeBoer
408ee41990 WIP: support configurable certificates/keys, and root trust CAs (#114)
* wip: tls options implemented in openssl

* update naming, remove #define guard

* assert compiled with USE_TLS for tls options

* apply autoformatter

* include tls options impl

* style cleanup; auto ssl_err

* ssl_err -> sslErr

* be explicit about SSL_VERIFY_NONE
2019-09-22 18:06:15 -07:00
Benjamin Sergeant
ed4be773a2 Fix crash in the Linux unittest in the HTTP client code, in Socket::readBytes. Cobra Metrics Publisher code returns the message id of the message that got published, to be used to validated that it got sent properly when receiving an ack. 2019-09-21 09:23:58 -07:00
Benjamin Sergeant
fcdb57f31d missing file in ws tool 2019-09-19 12:51:34 -07:00
Benjamin Sergeant
47b3368f78 In DNS lookup code, make sure the weak pointer we use lives through the expected scope (if branch) 2019-09-19 12:51:11 -07:00
Benjamin Sergeant
20ce498d23 On error while doing a client handshake, additionally display port number next to the host name 2019-09-17 12:08:52 -07:00
Benjamin Sergeant
354c9b412e rename test file 2019-09-17 12:07:31 -07:00
Benjamin Sergeant
1c08cedd8a make test target does not try to install anything into /usr/local 2019-09-12 11:45:31 -07:00
Benjamin Sergeant
e2121d809e fix unittest warnings + remove trailing spaces 2019-09-12 11:43:52 -07:00
Benjamin Sergeant
9c1065bc1b update pre-commit file 2019-09-10 22:18:16 -07:00
Benjamin Sergeant
27136bbce8 update clang format file 2019-09-10 22:17:08 -07:00
Benjamin Sergeant
c3238b7e02 build fixes 2019-09-10 14:05:07 -07:00
Benjamin Sergeant
b11640b477 update appveyor file to new directory structure 2019-09-10 12:33:47 -07:00
Benjamin Sergeant
2453f5b717 restructure project 2019-09-10 12:19:22 -07:00
Benjamin Sergeant
65c5c5f894 update ws CLI11 (our command line argument parsing library) to the latest, which fix a compiler bug about optional 2019-09-09 21:25:33 -07:00
Benjamin Sergeant
64d3c99f99 improve some websocket error messages + add a utility function with unittest to parse status line and stop using scanf which triggers warnings on Windows 2019-09-09 21:23:57 -07:00
Benjamin Sergeant
6c0890594b improve some websocket error messages + add a utility function with unittest to parse status line and stop using scanf which triggers warnings on Windows 2019-09-09 17:34:36 -07:00
Benjamin Sergeant
fb271953f7 websocket and http server: server does not close the bound client socket in many cases 2019-09-09 16:48:26 -07:00
Benjamin Sergeant
7080c5679f move poll wrapper on top of select (only used on Windows) to the ix namespace 2019-09-08 11:15:08 -07:00
Benjamin Sergeant
427db5bd59 Fix Windows CI with appveyor (#110)
Fix windows CI with appveyor + minor tweaks.
2019-09-07 14:07:00 -07:00
Benjamin Sergeant
c09eac49c9 README: update link to the doc 2019-09-06 10:42:48 -07:00
Benjamin Sergeant
ae6f87eb42 ci 2019-09-05 22:32:54 -07:00
Benjamin Sergeant
82b3c5e2f3 travis linux 2019-09-05 22:29:00 -07:00
Benjamin Sergeant
e41ba279e9 ci tweak / install redis 2019-09-05 22:14:55 -07:00
Benjamin Sergeant
c259c918ac cobra metrics publisher test uses random free port 2019-09-05 22:05:00 -07:00
Benjamin Sergeant
2f7438f0d5 cobra chat test uses random free port 2019-09-05 22:02:10 -07:00
Benjamin Sergeant
37a7b362d8 add cobra metrics publisher + server unittest 2019-09-05 21:57:05 -07:00
Benjamin Sergeant
c0f098a578 Add new cobra unittest, using server and client 2019-09-05 20:49:58 -07:00
Benjamin Sergeant
21404c23dd snake unsubscription fixes 2019-09-05 20:47:15 -07:00
Benjamin Sergeant
eeefc9cf4b Fix compiler warning 2019-09-05 20:29:14 -07:00
Benjamin Sergeant
24b2475b11 ws snake (cobra simple server) add basic support for unsubscription + subscribe send the proper subscription data + redis client subscription can be cancelled 2019-09-05 20:28:34 -07:00
Benjamin Sergeant
2defe6f597 IXCobraConnection / pdu handlers can crash if they receive json data which is not an object 2019-09-05 20:24:42 -07:00
Benjamin Sergeant
f9dc460325 cobra publish fix 2019-09-05 14:31:28 -07:00
Benjamin Sergeant
30b83b5ff0 Update README.md 2019-09-05 14:30:51 -07:00
Benjamin Sergeant
003afc8b56 all client autobahn test should pass ! last failing one was ...
+- zlib/deflate has a bug with windowsbits == 8, so we silently upgrade it to 9/ (fix autobahn test 13.X which uses 8 for the windows size)
2019-09-04 21:01:30 -07:00
Benjamin Sergeant
95a97a197a Fragmentation: for sent messages which are compressed, the continuation fragments should not have the rsv1 bit set (fix all autobahn tests for zlib compression 12.X)
Websocket Server / do a case insensitive string search when looking for an Upgrade header whose value is websocket. (some client use WebSocket with some upper-case characters)
2019-09-04 18:23:56 -07:00
Benjamin Sergeant
eccd8b3c0e ws autobahn / use condition variables for stopping test case + add more logging on errors 2019-09-04 12:21:54 -07:00
Benjamin Sergeant
a43046c921 ws autobahn / report progress with spdlog::info to get timing info 2019-09-04 10:16:32 -07:00
Benjamin Sergeant
b360fb9ca0 truncate module 2019-09-03 20:14:35 -07:00
Benjamin Sergeant
0bf185e143 Per message deflate/compression: handle fragmented messages (fix autobahn test: 12.1.X and probably others) 2019-09-03 17:42:48 -07:00
Benjamin Sergeant
da3d134be0 Receiving invalid UTF-8 TEXT message should fail and close the connection (fix remaining autobahn test: 6.X UTF-8 Handling) 2019-09-03 16:07:48 -07:00
Benjamin Sergeant
b4c4746d43 IXWebSocketTransport message processing refactoring 2019-09-03 15:48:55 -07:00
Benjamin Sergeant
fdd1ad9b17 Validate close codes. Autobahn 7.9.* 2019-09-03 15:43:16 -07:00
Benjamin Sergeant
1be8d9d46f Validate that the close reason is proper utf-8. Autobahn 7.5.1 2019-09-03 14:35:40 -07:00
Benjamin Sergeant
51799353a6 Sending invalid UTF-8 TEXT message should fail and close the connection (fix remaining autobahn test: 6.X UTF-8 Handling) 2019-09-03 14:12:40 -07:00
Benjamin Sergeant
3ad13a592d Framentation: data and continuation blocks received out of order (fix autobahn test: 5.9 through 5.20 Fragmentation) 2019-09-03 12:02:56 -07:00
Benjamin Sergeant
55934918ff Sending invalid UTF-8 TEXT message should fail and close the connection (fix **tons** of autobahn test: 6.X UTF-8 Handling) 2019-09-03 10:30:22 -07:00
Benjamin Sergeant
ab93e4f168 Message type (TEXT or BINARY) is invalid for received fragmented messages (fix autobahn test: 5.3 through 5.8 Fragmentation) 2019-09-03 09:13:38 -07:00
Benjamin Sergeant
e1ad0b0889 bump version 2019-09-02 10:14:15 -07:00
Benjamin Sergeant
cbe3e7617c Ping and Pong messages cannot be fragmented (autobahn test: 5.1 and 5.2 Fragmentation) 2019-09-02 10:13:40 -07:00
Benjamin Sergeant
94c8966e86 Close connections when reserved bits are used (autobahn test: 3 Reserved Bits) 2019-09-01 16:23:00 -07:00
Benjamin Sergeant
d973a062c2 changelog 2019-09-01 11:39:00 -07:00
Benjamin Sergeant
ba41dbc69a readme 2019-09-01 11:38:39 -07:00
Benjamin Sergeant
96380dd462 doc 2019-09-01 11:28:27 -07:00
Benjamin Sergeant
61bd765784 compute test case count properly 2019-09-01 11:17:28 -07:00
Benjamin Sergeant
4a0f06193b refactoring 2019-09-01 11:10:27 -07:00
Benjamin Sergeant
826917ef17 condition variable instead of busy looping 2019-09-01 10:50:16 -07:00
Benjamin Sergeant
4e1dbbbecf quiet mode 2019-09-01 10:45:51 -07:00
Benjamin Sergeant
b5b0de2083 +add utf-8 validation code, not hooked up properly yet
+ws autobahn / Add code to test websocket client compliance with the autobahn test-suite
+Ping received with a payload too large (> 125 bytes) trigger a connection closure
+cobra / add tracking about published messages
+cobra / publish returns a message id, that can be used when
+cobra / new message type in the message received handler when publish/ok is received (can be used to implement an ack system).
2019-08-31 16:47:10 -07:00
Benjamin Sergeant
a95fcbbdbf user agent 2019-08-30 12:50:56 -07:00
Benjamin Sergeant
7a73ec7c06 New option to cap the max wait between reconnection attempts. Still default to 10s. (setMaxWaitBetweenReconnectionRetries) (#108) 2019-08-30 12:46:35 -07:00
Benjamin Sergeant
0c1f2252a1 readme 2019-08-26 22:49:40 -07:00
Benjamin Sergeant
98a397696c readme 2019-08-26 22:29:10 -07:00
Benjamin Sergeant
225b7d7db7 doc / bring back detailed APIs 2019-08-26 22:11:35 -07:00
Benjamin Sergeant
f968d4c333 one last tweak 2019-08-26 22:02:24 -07:00
Benjamin Sergeant
59e15be524 readme tweaks 2019-08-26 21:57:05 -07:00
Benjamin Sergeant
ccabe93ae8 new simple readme 2019-08-26 21:55:00 -07:00
Benjamin Sergeant
56def6def4 Add md doc made with mkdocs 2019-08-26 21:25:45 -07:00
Benjamin Sergeant
3b1a1efed2 fix #104 - change ZLIB find_package to be optional 2019-08-26 14:51:33 -07:00
Benjamin Sergeant
185869e628 tentative gcc build fix 2019-08-26 14:29:16 -07:00
Benjamin Sergeant
108f6238e3 bump version 2019-08-26 10:20:01 -07:00
Benjamin Sergeant
d3e5a63fa2 ws connect has a new option to send HTTP headers + use WebSocketHttpHeaders instead of unordered_map<string, string> 2019-08-26 10:19:09 -07:00
Benjamin Sergeant
0847e60d2a CobraConnection: sets a unique id field for all messages sent to [cobra](https://github.com/machinezone/cobra).
CobraConnection: sets a counter as a field for each event published.
2019-08-26 09:51:37 -07:00
Benjamin Sergeant
ac60ec4320 put windows poll in the global namespace, not ix namespace 2019-08-26 09:51:37 -07:00
ozychhi
93debc00dc Add client handshake extra headers (#105)
Even though 6455 defines all the necessary headers needed for
client/server handshake, in practice most of the cases websocket servers
expect few more headers. Therefore adding this functionality.
2019-08-26 09:37:40 -07:00
Benjamin Sergeant
ff75846d2d Windows: use select instead of WSAPoll, through a poll wrapper 2019-08-22 10:34:17 -07:00
Benjamin Sergeant
53c767140d add new makefile target to make git tags 2019-08-20 09:21:30 -07:00
Benjamin Sergeant
839a747ce8 bump version + talk about Windows fix in the changelog 2019-08-20 09:20:02 -07:00
Benjamin Sergeant
f78a3f88ff add poll alias to WSAPoll on Windows 2019-08-19 22:26:25 -07:00
Benjamin Sergeant
142987259c fix #101 / wrong include in IXSocket.cpp on Windows 2019-08-19 22:19:39 -07:00
Benjamin Sergeant
c8d41f987f README tweaks 2019-08-19 20:35:26 -07:00
Benjamin Sergeant
d139dd88e8 README / formatting 2019-08-19 20:33:56 -07:00
Benjamin Sergeant
7898a5f4eb README.md: add reference to WSAStartup to initialize the networking system 2019-08-19 09:47:59 -07:00
Benjamin Sergeant
53efbf3ca9 (CI) linux: install libmbedtls 2019-08-14 21:49:43 -07:00
Benjamin Sergeant
b6e5ff2f3d (CI) try to build Linux on Ubuntu Bionic 2019-08-14 21:44:49 -07:00
Benjamin Sergeant
ae1386a1d7 bump version 2019-08-14 21:36:20 -07:00
Benjamin Sergeant
2f730303c2 CobraMetricThreadedPublisher _enable flag is an atomic, and CobraMetricsPublisher is enabled by default 2019-08-14 19:54:30 -07:00
Benjamin Sergeant
e98ec9ec75 clang format 2019-08-13 10:59:18 -07:00
Benjamin Sergeant
ffecef901a fix #99 / Connect error descriptions are invalid 2019-08-13 10:49:11 -07:00
Benjamin Sergeant
5c13cbb08f update readme 2019-08-06 20:55:44 -07:00
Benjamin Sergeant
64cfbe9514 fix #98 2019-08-02 17:11:53 -07:00
Benjamin Sergeant
5cc21c87fb new options for cobra commands
- ws cobra_subscribe has a new -q (quiet) option
- ws cobra_subscribe knows to and display msg stats (count and # of messages received per second)
- ws cobra_subscribe, cobra_to_statsd and cobra_to_sentry commands have a new option, --filter to restrict the events they want to receive
2019-08-01 15:22:24 -07:00
Benjamin Sergeant
505e0c79d9 ws connect command has a new option to send in binary mode (still default to text) 2019-07-25 15:48:45 -07:00
Benjamin Sergeant
ca9d59c1c1 add better line editing capability to ws connect, thanks to linenoise-cpp 2019-07-25 11:54:50 -07:00
Benjamin Sergeant
8319dbb56a README.md / cosmetic 2019-07-23 14:04:45 -07:00
Benjamin Sergeant
b1b6697c37 fix typo in README 2019-07-23 13:52:16 -07:00
Benjamin Sergeant
280716394d README: add reference to conan/vcpk to the build section 2019-07-22 20:41:06 -07:00
Benjamin Sergeant
5795f72eab do not update homebrew when installing a package 2019-07-03 14:49:39 -07:00
Benjamin Sergeant
7e16c8959b ci / get mbedtls from homebrew on mac 2019-07-03 14:46:05 -07:00
Benjamin Sergeant
dfc188a24d do not use mbed tls for the unittest 2019-07-03 14:39:46 -07:00
Benjamin Sergeant
d18bae0c95 add new docker file to run the unittest with tsan on latest Ubuntu 2019-06-30 23:37:25 -07:00
Benjamin Sergeant
747746cba1 dns / use cancellable instead of blocking 2019-06-30 23:26:14 -07:00
Benjamin Sergeant
5b73edec8c make IXDNSLookup more robust 2019-06-26 19:12:48 -07:00
Benjamin Sergeant
3750781bce simplify IXDNSLookup 2019-06-26 16:25:07 -07:00
Benjamin Sergeant
e646e53dac use poll instead of select in SocketServer 2019-06-25 17:18:24 -07:00
Benjamin Sergeant
6b8aa43ec0 switch from select to poll to deal with Android 9 giving us high socket fds when calling ::connect 2019-06-25 17:11:27 -07:00
Benjamin Sergeant
e8a20c7e8a refactor select code + add protection against large fds (cf Android 9) 2019-06-25 15:41:39 -07:00
Benjamin Sergeant
0423ed01a6 Add extra check in IXWebSocketCloseTest.cpp 2019-06-25 14:10:39 -07:00
Benjamin Sergeant
4a600c2611 select refactoring IXSocket::select -> IXSocket::poll 2019-06-25 10:16:40 -07:00
Benjamin Sergeant
e5faa23d4f disable CI on Windows 2019-06-25 00:28:11 -07:00
Benjamin Sergeant
b2f9c219b1 cmake use_tls fix 2019-06-24 23:34:31 -07:00
Benjamin Sergeant
77d65760f0 do not build mbedtls on ci 2019-06-24 23:28:35 -07:00
Benjamin Sergeant
98d0460af0 try to disable TLS for unittesting 2019-06-24 23:27:44 -07:00
Benjamin Sergeant
de8d93341c CI / build zlib and mbedtls locally 2019-06-24 23:17:19 -07:00
Benjamin Sergeant
0b6a773087 CI work 2019-06-24 10:17:57 -07:00
Benjamin Sergeant
dc1aa676c4 try to activate CI on windows again 2019-06-23 18:32:18 -07:00
Benjamin Sergeant
5ae9cc1cd7 bump docker version 2019-06-23 18:17:24 -07:00
Benjamin Sergeant
f84bc53c8d Feature/httpd (#94)
* Stub code for http server

* can send a response, cannot process body yet

* write headers to the response

* remove commented code

* add simple test + set default http handler

* tweak CI + unittest

* add missing file

* rewrite http::trim in a simple way

* doc
2019-06-23 14:54:21 -07:00
Benjamin Sergeant
b26e9d0338 IXDNSLookup. Uses weak pointer + smart_ptr + shared_from_this instead of static sets + mutex to handle object going away before dns lookup has resolved 2019-06-19 00:43:59 -07:00
Benjamin Sergeant
dbd62b8622 cobra_to_sentry / backtraces are reversed and line number is not extracted correctly 2019-06-13 10:18:40 -07:00
Benjamin Sergeant
20c80352bf disable windows on CI for now 2019-06-10 22:10:43 -07:00
Benjamin Sergeant
9d70fb2b86 disable building ws on windows on travis 2019-06-10 22:01:19 -07:00
Benjamin Sergeant
71aad26d44 (cmake) missing find_package(Threads) on UNIX 2019-06-10 13:39:22 -07:00
Benjamin Sergeant
3a1918eb2f Add -DUSE_VENDORED_THIRD_PARTY=1 to build ws 2019-06-10 13:26:41 -07:00
Benjamin Sergeant
a1709c07d9 - mbedtls and zlib are searched with find_package, and we use the vendored version if nothing is found 2019-06-10 11:18:27 -07:00
Benjamin Sergeant
b8c1176c79 - travis CI uses g++ on Linux 2019-06-09 14:27:45 -07:00
Benjamin Sergeant
03b5a57474 compile error in IXWebSocketMessageQTest 2019-06-09 12:25:36 -07:00
Benjamin Sergeant
f58cf4826a fix IXWebSocketMessageQTest.cpp 2019-06-09 12:08:00 -07:00
Benjamin Sergeant
d050cc5e13 fix changelog 2019-06-09 12:02:38 -07:00
Benjamin Sergeant
6ef88b6303 update README to reflect the new API 2019-06-09 12:02:02 -07:00
Benjamin Sergeant
3e15840b14 - WebSocket::send() sends message in TEXT mode by default
- WebSocketMessage sets a new binary field, which tells whether the received incoming message is binary or text
2019-06-09 11:56:47 -07:00
Benjamin Sergeant
0d147cbd94 WebSocket::send takes a third arg, binary which default to true (can be text too) 2019-06-09 11:35:31 -07:00
Benjamin Sergeant
be93f7480a WebSocket callback only take one object, a const ix::WebSocketMessagePtr& msg 2019-06-09 11:33:17 -07:00
Benjamin Sergeant
2e5f24f1f8 ... 2019-06-09 10:22:27 -07:00
Benjamin Sergeant
23cf4bd59b Add explicite WebSocket::sendBinary
New headers + WebSocketMessage class to hold message data, still not used across the board
2019-06-09 10:10:33 -07:00
Benjamin Sergeant
051c34bc5d Add test/compatibility folder with small servers and clients written in different languages and different libraries to test compatibility. 2019-06-08 09:46:26 -07:00
Benjamin Sergeant
9623ceb4d5 ws echo_server has a -g option to print a greeting message on connect 2019-06-08 09:16:33 -07:00
Benjamin Sergeant
e1a7395880 IXSocketMbedTLS: better error handling in close and connect 2019-06-06 14:59:22 -07:00
Benjamin Sergeant
51aeeca024 add a changelog 2019-06-06 13:59:12 -07:00
Benjamin Sergeant
076e8bf6a3 add an option to easily disable per message deflate compression 2019-06-06 13:48:53 -07:00
Benjamin Sergeant
73c5b9b847 fix Dockerfile link 2019-06-05 19:38:44 -07:00
Benjamin Sergeant
f4f3eed78d cobra to sentry / more error handling 2019-06-05 19:37:51 -07:00
Benjamin Sergeant
89909c15bc cobra to sentry fixes 2019-06-05 18:47:48 -07:00
Benjamin Sergeant
78b3d7ff2d Feature/http async (#90)
* unittest working / uses shared_ptr for a bunch of things 🗿

* fix command line tools

* fix ws + add doc

* add more logging
2019-06-05 17:04:24 -07:00
Benjamin Sergeant
012193c74e Fix compile error with JSON uint64_t 🚯 2019-06-04 13:45:29 -07:00
Benjamin Sergeant
539abe5151 HttpResponse is a struct, not a tuple 🉐 2019-06-03 22:12:52 -07:00
Benjamin Sergeant
7e5aba140e http / PUT fix 🐚 2019-06-03 21:12:39 -07:00
Benjamin Sergeant
6b933391e5 http client: stop hardcoding Accept header, and use a default value if one is passed in 👭 2019-06-03 14:02:54 -07:00
Benjamin Sergeant
1e2a5ee21d Add simple HTTP and HTTPS client test ㊙️ 2019-06-03 12:23:35 -07:00
Benjamin Sergeant
225a5ef808 (http client) / Add DEL and PUT method, make requests and other utilities public 👐 2019-06-03 11:38:56 -07:00
Benjamin Sergeant
396d0d9bdc Feature/ws windows (#86)
* try to build ws on window on travis 📮

* cmake invocation fixed on windows 🐝

* Can use mbedtls to calculate hmac + no openssl config option

* build only windows on travis 🕢

* run msbuild 💷

* proper command to build 🕛

* some build fixes

* change weird sizeof call 🐙

* warning and missing includes fixes 💮

* ifdef out statsd code on windows 🐍

* logic invertion in ifdef 👄

* bring back makefile 📜

* command line tool is built with mbedtls 🏥
2019-06-02 20:46:20 -07:00
Benjamin Sergeant
88c8fb74bb play with podmena 2019-06-02 11:03:44 -07:00
Benjamin Sergeant
fd902c7a58 add notes about mbedtls CMake 2019-06-01 18:00:25 -07:00
Benjamin Sergeant
06cbebe22e Feature/mbedtls (#84)
* try to import mbedtls and build it

* add stubs socket class

* some boilterplate, read and write function implemented

* more boilterplate / current error in handshake because no CA cert is setup

* add something so skip ca verification, can ws curl https://google.com !

* cleanup / close implemented

* tweak CMakefiles

* typo in include

* update readme

* disable unittests
2019-06-01 17:41:48 -07:00
Benjamin Sergeant
ba4a9e1586 (clang format) fix indent and make (rarely) accessor/setters in class on a single line 2019-05-31 00:53:14 -07:00
Benjamin Sergeant
285386e47f fix cobra to sentry + change ws docker file to use alpine (much smaller footprint) 2019-05-31 00:43:22 -07:00
Benjamin Sergeant
c65fec7271 clang format, based on cpprest 2019-05-30 08:46:50 -07:00
Benjamin Sergeant
879a4b38aa std::chrono::duration is not initialized to 0 units of time 2019-05-26 14:16:15 -07:00
Benjamin Sergeant
13c87e38ed do not select on a closed file descriptor (doing so crash on Android) 2019-05-22 18:58:22 -07:00
Benjamin Sergeant
718154cfb4 enable IXWebSocketMessageQTest.cpp on mac and windows 2019-05-22 11:03:13 -07:00
Kumamon38
26de9b9714 Clean (#82)
Thanks
2019-05-21 12:14:58 -07:00
Kumamon38
3365facf9f Fix ping (#80)
* let poll do his job when closing

* try fix test

* try fix test

* Update IXWebSocketTransport.cpp

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* change state immediately, and send close frame after

* add immediate close test

* disable test for windows

* reenable ping / ping timeout tests

* add time to let windows close client

* reenable ping timeout test

* add 100ms more

* disable test for windows
2019-05-21 09:35:41 -07:00
Kumamon38
8a4826164b fix close bug and tests : let poll do his job when closing (#79)
* let poll do his job when closing

* try fix test

* try fix test

* Update IXWebSocketTransport.cpp

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* change state immediately, and send close frame after

* add immediate close test

* disable test for windows
2019-05-21 09:34:08 -07:00
Benjamin Sergeant
d6eabae4f0 one cpu on windows for executing tests 2019-05-17 15:45:31 -07:00
Benjamin Sergeant
6bd81bb92e add back IXWebSocketMessageQueue, with its unittest disabled 2019-05-16 22:41:39 -07:00
Benjamin Sergeant
126a91dfec Linux build fix: strncpy needs <string.h> 2019-05-16 22:21:15 -07:00
Benjamin Sergeant
51fa147b99 Revert "Merge branch 'Dimon4eg-message-queue'"
This reverts commit 13fa325134163997fe73a5b0658b8301c9b2f729, reversing
changes made to aecd5e9c944ce9b53533a846790ae4d383fa924b.
2019-05-16 22:15:17 -07:00
Benjamin Sergeant
6160877167 Fix Address Sanitizer heap-buffer-overflow in WebSocketHandshakeKeyGen::generate
=================================================================
==5077==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6070000077e0 at pc 0x00010ba18c54 bp 0x70000dd45b10 sp 0x70000dd45b08
READ of size 1 at 0x6070000077e0 thread T12
    #0 0x10ba18c53 in WebSocketHandshakeKeyGen::generate(char const*, char*) libwshandshake.hpp:113
    #1 0x10ba2065a in ix::WebSocketHandshake::serverHandshake(int, int) IXWebSocketHandshake.cpp:356
    #2 0x10b9c4952 in ix::WebSocketTransport::connectToSocket(int, int) IXWebSocketTransport.cpp:190
    #3 0x10b97e4c2 in ix::WebSocket::connectToSocket(int, int) IXWebSocket.cpp:193
2019-05-16 21:59:03 -07:00
Benjamin Sergeant
717f049579 Merge branch 'Dimon4eg-message-queue' 2019-05-16 19:26:45 -07:00
Benjamin Sergeant
f71331056c bring back socket mutex which is needed, some CI failures are happening without it 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
c131ff2662 disable socket mutex usage in WebSocketTransport 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
616447e01d remove dead code 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
8c1d66bcf3 refactor connect unittest so that it hits a local server instead of a remote server 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
bea580b906 travis makefile fix 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
c513e02b24 try to run ws test on linux + macOS on travis 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
90d71deb0f Add constants for closing code and messages 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
fc0776303a first socket test hit a local server instead of a remote server / this can help with a windows intermittent failure 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
bb0c6f9a8a build ws on travis (mac + linux) 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
dae21e7681 use a regular mutex instead of a recursive one + stop properly 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
d28437ecc0 adding logging to IXWebSocketTestConnectionDisconnection makes it fails reliably 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
7fec24af67 Update README.md 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
0de3637569 Update README.md 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
f94c7cef59 -j option actually work ... 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
7734d63b1b unittest / add options to set the number of jobs 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
f894504761 enum class HttpErrorCode derives from int 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
7aa9b4ee64 try to re-enable some tests 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
a12250dc16 recursive mutex + enable test that was breaking on Ubuntu Xenial + gcc + tsan 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
d8fbe1a63e comment failing test 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
91e1760719 comment failing test 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
02c8a62e7d do not build ws for now on travis 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
0c9bcfb8ac more protection against socket when closing 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
bd4c5037c7 fix compile errors with C++ enum class 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
ccaaedf38f close and stop with code and reason + docker = ubuntu xenial 2019-05-16 19:23:32 -07:00
Dimon4eg
751f294164 add isEnabledAutomaticReconnection (#75)
* add isEnabledAutomaticReconnection

* test isEnabledAutomaticReconnection

* rename
2019-05-16 19:23:32 -07:00
Benjamin Sergeant
e2acbe8499 Revert "Revert "fix cast warning caught on windows""
This reverts commit 25eaf730bc93478393dea57b17b32cda0a9b6a60.
2019-05-16 19:23:32 -07:00
Benjamin Sergeant
aba880a6b3 Revert "fix cast warning caught on windows"
This reverts commit 4edb7447df0c705e9c393da390de6a9980b3fe09.
2019-05-16 19:23:32 -07:00
Benjamin Sergeant
616e8da0a5 fix cast warning caught on windows 2019-05-16 19:23:32 -07:00
Kumamon38
a220774a3b add more time to let client close (#73) 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
4fc8224264 build with gcc on Linux 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
66dae5840c use spdlog for logging in the unittest 2019-05-16 19:23:32 -07:00
Benjamin Sergeant
89b9e6e531 fix inverted conditional 2019-05-16 19:23:31 -07:00
Benjamin Sergeant
fc4623381a server code / add dedicated thread to close/join terminated connection threads 2019-05-16 19:23:31 -07:00
Benjamin Sergeant
1023e925f6 travis cmake version on macOS does not know --parallel option 2019-05-16 19:23:31 -07:00
Benjamin Sergeant
5d65365751 build in parallel + stop building linux + clang 2019-05-16 19:23:31 -07:00
Benjamin Sergeant
ee64a6ec7e disable ping tests for now as they are not super reliable 2019-05-16 19:23:31 -07:00
Benjamin Sergeant
9bc09105d7 Feature/windows ci (#76)
* close with params

* ...

* different generator

* core size = 1

* disable more tests to get something working on windows

* try to enable another test on windows

* enable all OS

* set proper version of linux

* another try

* try again with just env variables

* Revert "core size = 1"

This reverts commit 29af74bba68df093158179e85039dc1580c3b68a.

* add windows and mac

* Revert "close with params"

This reverts commit 6bb00b6788fa453e64f514220e1dedac553cea17.
2019-05-16 19:23:31 -07:00
Kumamon38
4b96632a69 save timepoints after connect and not in contructor, adjusted tests (#72)
* save timepoints after connect and not in contructor, adjusted tests

* move call into setReadyState

* more time to detect client close in test
2019-05-16 19:20:29 -07:00
Dimon4eg
22a806ca6f fix for Windows (#69)
* fix for Windows

* fix condition

* make condition only on Windows
2019-05-16 19:20:29 -07:00
Dimon4eg
d8dc977fc1 Fix run.py (#71)
* fix run.py

* run.py: fix Windows support

* fix test listing
2019-05-16 19:20:29 -07:00
Benjamin Sergeant
6d900b8ffb bring back socket mutex which is needed, some CI failures are happening without it 2019-05-16 15:58:20 -07:00
Benjamin Sergeant
5a2c070898 disable socket mutex usage in WebSocketTransport 2019-05-16 15:46:32 -07:00
Benjamin Sergeant
58f17ddb09 remove dead code 2019-05-16 15:05:20 -07:00
Benjamin Sergeant
47c9786bab refactor connect unittest so that it hits a local server instead of a remote server 2019-05-16 14:25:31 -07:00
Benjamin Sergeant
e5edbeacb4 travis makefile fix 2019-05-16 14:02:24 -07:00
Benjamin Sergeant
964fb20df9 try to run ws test on linux + macOS on travis 2019-05-16 13:57:33 -07:00
Benjamin Sergeant
309ed80446 Add constants for closing code and messages 2019-05-16 12:46:53 -07:00
Benjamin Sergeant
01f2eb6615 first socket test hit a local server instead of a remote server / this can help with a windows intermittent failure 2019-05-16 12:24:58 -07:00
Benjamin Sergeant
3a55c7aaba build ws on travis (mac + linux) 2019-05-16 07:01:15 -07:00
Benjamin Sergeant
243f41bf28 use a regular mutex instead of a recursive one + stop properly 2019-05-15 19:26:02 -07:00
Benjamin Sergeant
934b28f5b6 adding logging to IXWebSocketTestConnectionDisconnection makes it fails reliably 2019-05-15 19:26:02 -07:00
Benjamin Sergeant
edfc03bed2 Update README.md 2019-05-15 19:22:05 -07:00
Benjamin Sergeant
59ce71b64d Update README.md 2019-05-15 18:57:17 -07:00
Benjamin Sergeant
d473a7dc22 -j option actually work ... 2019-05-15 18:15:45 -07:00
Benjamin Sergeant
efb063d600 unittest / add options to set the number of jobs 2019-05-15 17:52:03 -07:00
Benjamin Sergeant
12fe55905c enum class HttpErrorCode derives from int 2019-05-15 16:50:00 -07:00
Benjamin Sergeant
aed831c075 try to re-enable some tests 2019-05-15 16:28:29 -07:00
Benjamin Sergeant
5ad15fad8b recursive mutex + enable test that was breaking on Ubuntu Xenial + gcc + tsan 2019-05-15 16:01:05 -07:00
Benjamin Sergeant
a7d328896c comment failing test 2019-05-15 15:44:14 -07:00
Benjamin Sergeant
1274a151d0 comment failing test 2019-05-15 15:37:30 -07:00
Benjamin Sergeant
d93d639345 do not build ws for now on travis 2019-05-15 15:26:49 -07:00
Benjamin Sergeant
e0d9a16985 more protection against socket when closing 2019-05-15 15:18:46 -07:00
Benjamin Sergeant
7f1070dde6 fix compile errors with C++ enum class 2019-05-15 15:18:46 -07:00
Benjamin Sergeant
7f1e70329c close and stop with code and reason + docker = ubuntu xenial 2019-05-15 15:18:46 -07:00
Dimon4eg
186c8fbb62 add isEnabledAutomaticReconnection (#75)
* add isEnabledAutomaticReconnection

* test isEnabledAutomaticReconnection

* rename
2019-05-14 11:26:37 -07:00
Benjamin Sergeant
c935be6a49 Revert "Revert "fix cast warning caught on windows""
This reverts commit 25eaf730bc93478393dea57b17b32cda0a9b6a60.
2019-05-13 22:16:49 -07:00
Benjamin Sergeant
4ee502fa1a Revert "fix cast warning caught on windows"
This reverts commit 4edb7447df0c705e9c393da390de6a9980b3fe09.
2019-05-13 21:35:34 -07:00
Benjamin Sergeant
084805b248 fix cast warning caught on windows 2019-05-13 21:29:47 -07:00
Kumamon38
eaebd258c0 add more time to let client close (#73) 2019-05-13 21:26:34 -07:00
Benjamin Sergeant
2843a20814 build with gcc on Linux 2019-05-13 17:35:21 -07:00
Benjamin Sergeant
08a56726a8 use spdlog for logging in the unittest 2019-05-13 17:32:57 -07:00
Benjamin Sergeant
7cad8654e5 fix inverted conditional 2019-05-13 17:18:07 -07:00
Benjamin Sergeant
841cfe37dd server code / add dedicated thread to close/join terminated connection threads 2019-05-13 17:17:35 -07:00
Benjamin Sergeant
849a41293f travis cmake version on macOS does not know --parallel option 2019-05-13 17:17:35 -07:00
Benjamin Sergeant
5b17edb3f9 build in parallel + stop building linux + clang 2019-05-13 17:06:56 -07:00
Benjamin Sergeant
9f2047dad6 disable ping tests for now as they are not super reliable 2019-05-13 17:01:22 -07:00
Benjamin Sergeant
c01c53c5c7 Feature/windows ci (#76)
* close with params

* ...

* different generator

* core size = 1

* disable more tests to get something working on windows

* try to enable another test on windows

* enable all OS

* set proper version of linux

* another try

* try again with just env variables

* Revert "core size = 1"

This reverts commit 29af74bba68df093158179e85039dc1580c3b68a.

* add windows and mac

* Revert "close with params"

This reverts commit 6bb00b6788fa453e64f514220e1dedac553cea17.
2019-05-13 16:51:58 -07:00
Kumamon38
27bf1684cb save timepoints after connect and not in contructor, adjusted tests (#72)
* save timepoints after connect and not in contructor, adjusted tests

* move call into setReadyState

* more time to detect client close in test
2019-05-13 09:08:46 -07:00
Dimon4eg
be2aee3354 fix for Windows (#69)
* fix for Windows

* fix condition

* make condition only on Windows
2019-05-12 22:21:56 -07:00
Dimon4eg
5f42a07d0d Fix run.py (#71)
* fix run.py

* run.py: fix Windows support

* fix test listing
2019-05-12 18:37:22 -07:00
dimon4eg
8a94c945b7 Merge branch 'master' into message-queue 2019-05-12 22:00:10 +03:00
Benjamin Sergeant
7740028291 all derived class use final keyword 2019-05-12 11:43:21 -07:00
dimon4eg
7369e9c233 add setOnMessageCallback with r-value 2019-05-12 20:59:18 +03:00
dimon4eg
8c66825a78 correct style 2019-05-12 20:16:02 +03:00
dimon4eg
a56f8272a9 fix warnings 2019-05-12 20:05:28 +03:00
dimon4eg
e846ca392f update comment 2019-05-12 20:00:15 +03:00
dimon4eg
f9ec89cf7a update test 2019-05-12 19:57:31 +03:00
dimon4eg
b935bc526a added message queue test 2019-05-12 01:50:41 +03:00
dimon4eg
97617ced4a Added WebSocketMessageQueue 2019-05-12 01:49:06 +03:00
Benjamin Sergeant
d575c7c2a9 bump version to 2.0.0 2019-05-11 14:22:41 -07:00
Dimon4eg
99a3bbc4f9 use C++11 enums (#67)
* use C++11 enums

* small rename

* update tests

* update tests

* update ws

* update ws

* update README.md
2019-05-11 14:22:06 -07:00
Benjamin Sergeant
80226cb7d3 add comment about why a unittest is disabled 2019-05-11 12:25:40 -07:00
Benjamin Sergeant
6189e0cd50 remove irrelevant comment 2019-05-11 12:24:11 -07:00
Dimon4eg
2254421ead minor improvements (#66)
* minor improvements

* fix build

* improve tests code
2019-05-11 12:20:58 -07:00
Benjamin Sergeant
4934f5846b minor tweaks to have full feature parity before unittest broke 2019-05-11 11:54:21 -07:00
Benjamin Sergeant
c8c1aabf20 fix race condition in SelectInteruptPipe, where _fildes are not protected (caught by fedora tsan) 2019-05-11 11:45:26 -07:00
Dimon4eg
93b901a286 remove more iostream includes (#65) 2019-05-11 11:27:58 -07:00
Benjamin Sergeant
518a445074 rename some variables, minor cleanup 2019-05-11 10:24:28 -07:00
dimon4eg
080a6db657 uncomment test 2019-05-11 10:15:22 -07:00
dimon4eg
d49b1bd78a fix crash on close 2019-05-11 10:15:22 -07:00
Dimon4eg
bd96050d84 fix crash on close 2019-05-11 10:12:33 -07:00
Dimon4eg
2a90ad9e53 Fix crash during closing on Windows (#64)
* fix crash on close

* Improve calculateRetryWaitMilliseconds (#63)

* improve calculateRetryWaitMilliseconds

* update comment

* cout -> spdlog

* fix crash on close

* uncomment test

* Revert "uncomment test"

This reverts commit 27df86ee8f9eb1e0e14652d0707faab1aff88001.
2019-05-11 09:51:26 -07:00
Benjamin Sergeant
16758293ff fix warning in statsd_client about %m gnu only printf special char 2019-05-11 09:22:29 -07:00
Benjamin Sergeant
e965322a98 fix overflow warning in msgpack11.cpp 2019-05-10 21:17:05 -07:00
Benjamin Sergeant
fcf5c41b43 add env var to display the ws command typed in 2019-05-10 16:27:23 -07:00
Benjamin Sergeant
88adbf0ca2 cout -> spdlog 2019-05-10 12:33:34 -07:00
Dimon4eg
3df142db7a Improve calculateRetryWaitMilliseconds (#63)
* improve calculateRetryWaitMilliseconds

* update comment
2019-05-10 12:31:21 -07:00
Benjamin Sergeant
f90fc4bfa2 fix static analyzer thing with un-used variable 2019-05-09 16:57:58 -07:00
Benjamin Sergeant
dc1f9fb243 use spdlog for logging in ws + unittest + remove un-needed mutex 2019-05-09 15:30:44 -07:00
Benjamin Sergeant
7c30c8aa07 cout -> cerr 2019-05-09 15:06:42 -07:00
tiwariashish86
b1d13105e7 Fixed pong synchronization issue (#62)
* Fixed pong synchronization issue

* Minor optimization in lock by scoping it to necessary changes.

* Fixing compilation issues
2019-05-09 15:06:05 -07:00
Benjamin Sergeant
287537b34a tweak unittest sleep duration to fix gcc+linux CI 2019-05-09 11:10:39 -07:00
Kumamon38
fe04014e1c Fail test GCC TSAN (#61)
* test that fails on Run 8

* commented the failing test
2019-05-09 09:33:18 -07:00
Benjamin Sergeant
094b5834b7 warning police 2019-05-09 09:25:56 -07:00
Kumamon38
1eb98cc74f fix ping, fix send frame close (#49)
* fix ping, fix send frame close

* fixes for data race on _closeCode etc. and fix test

* fixing one TC

* fix waiting forever if no time to change of readyState, and poll never end

* add 1005 code if no status code received

* fixes for 1005 code

* fix test issue

* fix macOS issue

* revert to master tests and renaming
2019-05-09 09:21:05 -07:00
Benjamin Sergeant
232aa069d2 IXCobraMetricsThreadedPublisher.cpp uses a lambda to log instead of std::cerr 2019-05-08 18:53:32 -07:00
dimon4eg
a69408fa25 rename ptr 2019-05-09 01:05:47 +03:00
dimon4eg
75011d0b4e simplify bindWebsocket 2019-05-09 00:23:16 +03:00
dimon4eg
28ae70ed20 use lock_guard 2019-05-09 00:20:26 +03:00
dimon4eg
2727d39fa4 update comments 2019-05-09 00:16:37 +03:00
dimon4eg
467f99b3bb Rename to WebSocketMessageQueue 2019-05-09 00:09:51 +03:00
Benjamin Sergeant
d53c9c5ecf change default ports for the ws command line tool 2019-05-08 13:56:42 -07:00
Benjamin Sergeant
48c19b4f3c ws connect display more accurate messages for incoming messages 2019-05-08 13:56:42 -07:00
Dimon4eg
16e5b08a0f Remove redundant iostream includes (#60) 2019-05-08 13:33:21 -07:00
Dimon4eg
636a69e9e1 qf 2019-05-08 22:24:39 +03:00
Dimon4eg
45d40dc159 Added IXWebSocketPoll class 2019-05-08 22:02:56 +03:00
Dimon4eg
88abb79a96 set thread name for Windows (#57) 2019-05-08 07:43:43 -07:00
Benjamin Sergeant
1e1d5c3f7d update appveyor windows build file 2019-05-06 17:50:55 -07:00
Dimon4eg
95e9faff95 update README.md (#54)
Yeah !
2019-05-06 15:02:16 -07:00
Dimon4eg
979ff60a6b Use LUrlParser to fix issue of Windows (#53)
LGTM
2019-05-06 14:45:02 -07:00
Dimon4eg
ea2e8f0787 added tests for IXUrlParser (#52)
* added tests for IXUrlParser

* add me as author
2019-05-06 12:47:15 -07:00
Benjamin Sergeant
3893c12054 socket server / used wrong mutex to protect _connectionsThreads 2019-05-06 12:24:20 -07:00
Dimon4eg
8ad47a315b Fix windows (#51)
* More fixes for Windows

* fix tests for windows

* qf for linux

* clean up
2019-05-06 12:22:57 -07:00
Benjamin Sergeant
3b576c3047 Update README.md 2019-05-06 09:22:52 -07:00
Dimon4eg
4d83dab4f3 Fix for windows (#50) 2019-05-06 09:13:42 -07:00
Benjamin Sergeant
28a7ec4f35 doc: add reference to the conan file built at https://github.com/Zinnion/conan-IXWebSocket 2019-05-01 21:31:32 -07:00
Benjamin Sergeant
b5aae88a0b remove un-needed _backgroundThreadRunning variable 2019-05-01 11:09:25 -07:00
Benjamin Sergeant
bee97237d9 Make AutomaticReconnection optional (#47)
* unittest pass + commands behave as expected

* cleanup
2019-04-29 21:12:34 -07:00
Benjamin Sergeant
8c8e950455 build fix 2019-04-29 20:54:00 -07:00
Benjamin Sergeant
ad8b344298 tsan fixes on ubuntu xenial (what travis run) 2019-04-29 20:48:16 -07:00
Benjamin Sergeant
6d310d417a dns lookup: fix race condition accessing _errMsg 2019-04-29 19:29:27 -07:00
Benjamin Sergeant
9dca893ce7 tsan linux tentative fix / copy string instead of passing a const reference 2019-04-29 17:27:53 -07:00
Benjamin Sergeant
e3444e666b rename _blocking to _backgroundThreadRunning and invert the naming 2019-04-29 16:54:08 -07:00
Alexandre Konieczny
e37e69311b fix data race on _thread 2019-04-29 16:46:16 -07:00
Alexandre Konieczny
6918f863b1 fix data race on _useMask 2019-04-29 16:41:34 -07:00
Benjamin Sergeant
9ee05bf591 ws connect mode / add a flag to disable automatic reconnection, not hooked up yet 2019-04-29 14:31:29 -07:00
Benjamin Sergeant
e15700235e enable tsan on travis for all configs 2019-04-29 09:11:16 -07:00
Benjamin Sergeant
1c7c07e128 initialize netSystem (aka winsock on windows) explicitely 2019-04-25 16:38:15 -07:00
Dimon4eg
4fbc4e3be9 Fixes for windows (#45)
* init Net system on Windows

* propagate DNS error

* Add zlib 1.2.11 sources

* link zlib statically for windows

* remove not implemented function declaration

* fix connect on Windows
2019-04-25 16:26:53 -07:00
Benjamin Sergeant
e251c81d43 fix indentation of greatestCommonDivisor 2019-04-25 16:21:36 -07:00
Benjamin Sergeant
f30a5074ab Remove commented code 2019-04-25 16:16:52 -07:00
Benjamin Sergeant
f6ae490723 Fix data race in WebSocket where _url is accessed without protection in setThreadName
Also fix with url usage + docker container uses fedora and works with tsan
2019-04-25 16:11:46 -07:00
Benjamin Sergeant
7f96c43d6f disable failing unittest temporarily 2019-04-25 09:04:35 -07:00
Dimon4eg
52260a63fb Speedup build for Windows (#43)
* Speedup build for Windows

* add space :)
2019-04-25 07:41:01 -07:00
Benjamin Sergeant
82b528ee30 tsan fix for the IXWebSocketServerTest test, where there's a data race for connectionId 2019-04-24 22:11:14 -07:00
Benjamin Sergeant
a443bbdf80 Fix data races in DNSLookup (tsan) 2019-04-24 21:53:31 -07:00
Benjamin Sergeant
26ee46b246 better server termination / another try at preventing thread join failures 2019-04-24 09:45:53 -07:00
Benjamin Sergeant
cf37816602 compiler warning police 2019-04-24 09:45:03 -07:00
Benjamin Sergeant
b8087f6c48 Fix #38 Add some docker doc in the README 2019-04-23 20:51:58 -07:00
Benjamin Sergeant
28cbe8fbeb ws cobra publish stress mode fix 2019-04-23 20:51:58 -07:00
Kumamon38
28210ee31d add boolean and add missing protocol error close constant (#41) 2019-04-23 04:31:55 -07:00
Benjamin Sergeant
0caf875399 add example websocket C++ server snake which supports basic cobra ops (publish and subscribe without stream sql 2019-04-22 17:33:45 -07:00
Benjamin Sergeant
323684efff (server) attempt to fix broken macOS unittest on travis CI 2019-04-22 09:36:16 -07:00
Benjamin Sergeant
678ee0615d Expand build section in the main README 2019-04-21 21:11:08 -07:00
Benjamin Sergeant
6c889def37 Merge branch 'dhruvkakadiya-readme/fix' 2019-04-21 16:14:35 -07:00
dhruvkakadiya
93586deb6f For #39, fixed setOnMessageCallback() in README. 2019-04-21 14:56:02 -07:00
Benjamin Sergeant
b6b9ffd15c doc cobra 2019-04-21 11:52:38 -07:00
Benjamin Sergeant
1189b5f693 new target to uninstall files 2019-04-21 11:47:57 -07:00
Benjamin Sergeant
1c2b6d59da Fix #37 / add directives to install headers and library 2019-04-21 11:42:37 -07:00
Benjamin Sergeant
eacc28fedf move cobra files to their own subfolder 2019-04-21 11:20:17 -07:00
Benjamin Sergeant
5c85ee1214 add cobra metrics publisher 2019-04-21 11:16:33 -07:00
Benjamin Sergeant
7df7453365 indentation / comestic changes 2019-04-19 16:57:38 -07:00
Benjamin Sergeant
3d8297247e (test) / use a random number generator to get a free port, when the bind to port 0 strategy does not work out 2019-04-19 16:50:04 -07:00
Benjamin Sergeant
662f66e501 (socket server) wait for all connections threads to be terminated before exiting stop method 2019-04-19 16:31:33 -07:00
Benjamin Sergeant
9131cb4790 default sanitizer choice 2019-04-19 15:13:59 -07:00
Benjamin Sergeant
3b616676c6 disable clang sanitizers in CI on any platforms but Darwin 2019-04-19 15:09:01 -07:00
Benjamin Sergeant
cce759b8dd fix Linux cast warning 2019-04-19 15:03:49 -07:00
Benjamin Sergeant
98b6c9b89e (unittest) upgrade to Catch2 version 2.7.1 2019-04-19 14:41:03 -07:00
Benjamin Sergeant
5370201df8 add locks around Socket::send and Socket::recv to see if it helps with thread sanitizer error in Linux CI 2019-04-19 14:28:51 -07:00
Benjamin Sergeant
419c395966 uses sh syntax to capture output 2019-04-19 12:40:39 -07:00
Benjamin Sergeant
2962ce9a0f (ci) verbose mode to figure out Linux build problems on travis 2019-04-19 12:10:43 -07:00
Benjamin Sergeant
c96398aa0c Windows nmake does not have a -j flag 2019-04-19 11:58:02 -07:00
Benjamin Sergeant
e68ce1d680 Windows + unittest python script fixes 2019-04-19 11:54:58 -07:00
Benjamin Sergeant
d34f10b4ea fix warning / ws_cobra_subscribe does not need a verbose flag 2019-04-19 11:45:42 -07:00
Benjamin Sergeant
7e2c1f274b (test) build dir is an absolute path 2019-04-19 11:45:02 -07:00
Benjamin Sergeant
9fe3811c45 (ws) fix compiler warnings 2019-04-19 09:48:46 -07:00
Benjamin Sergeant
b74bccee0a fix test execution on travis which was broken / unify running test locally and on travis 2019-04-19 09:46:17 -07:00
Benjamin Sergeant
a2d170f415 fix bad merge in IXWebSocketTransport.cpp ... 2019-04-19 09:41:16 -07:00
Benjamin Sergeant
03f762db86 Socket::Poll does not need a callback 2019-04-19 09:32:49 -07:00
Kumamon38
aea859af52 Ping timeout use constant (#36)
* use constant for ping timeout

* change close code types
2019-04-19 09:16:25 -07:00
Dimon4eg
f61fd7b7f1 Link zlib statically for windows (#35)
* Add zlib 1.2.11 sources

* link zlib statically for windows
2019-04-19 09:14:03 -07:00
Kumamon38
5682129b1d fix close code/reason issue (#34)
* fix close code/reason issue

* added code 1006 for abnormal closure
2019-04-18 10:02:31 -07:00
Kumamon38
c3431f19bf Real ping (#32)
* close method change and fix code

* missing mutex

* wip

* renaming and fixes

* renaming, fixes

* added enablePong/disablePong, add tests

* added new test cases

* add 1 test case

* fix gcd name to greatestCommonDivisor

* move ping and ping timeout checks into socket poll, local var in test cases and indent fixes

* indent issue
2019-04-18 09:24:16 -07:00
Benjamin Sergeant
65b11cb968 docker container works with SSL + fix compiler warnings in statsd third_party module 2019-04-18 09:11:12 -07:00
Benjamin Sergeant
f4f60d38b8 docker + linux build fix 2019-04-17 22:52:03 -07:00
Benjamin Sergeant
4337345103 setter method does not need to return anything, make it void 2019-04-17 20:36:26 -07:00
Benjamin Sergeant
52f460f66d (doc) Add more doc to SocketServer 2019-04-17 20:36:26 -07:00
Benjamin Sergeant
d486c72e02 websocket server: closed connection threads are joined properly 2019-04-17 20:36:26 -07:00
Benjamin Sergeant
bdfc55b951 Bug/30 server connection problem (#31)
* use threads instead of std::async, need to cleanup threads

* less buggy server connection per thread system
2019-04-16 22:19:44 -07:00
Benjamin Sergeant
b0f6026c23 make closeWireSize a default parameter of WebSocketTransport::close 2019-04-16 09:55:12 -07:00
Kumamon38
b2aca491b6 close method change and fix code (#28)
* close method change and fix code

* missing mutex
2019-04-16 08:58:34 -07:00
Dimon4eg
401fc39879 fix windows build (#29)
* fix windows build

* fix for Unix

* Fix build if TLS is OFF

* add OpenSSL req to ws

* Fix build on Mac

* fix tests for windows
2019-04-16 08:51:57 -07:00
Dimon4eg
d4b0839328 move security framework linking to ixwebsocket (#26) 2019-04-14 17:13:24 -07:00
Benjamin Sergeant
37c64841ff fix warning 2019-04-13 21:16:04 -07:00
Benjamin Sergeant
8f8dd076ff ws: new command to subscribe to a cobra server and send an event to a sentry server 2019-04-11 16:03:05 -07:00
Benjamin Sergeant
51fcf65424 (ws) add subcommands: cobra subscribe, and cobra subscribe to statsd bridge 2019-04-08 21:56:01 -07:00
Benjamin Sergeant
56b19fa2b0 linux ci tentative fix 2019-04-03 22:02:10 -07:00
Benjamin Sergeant
fe38dab405 better unittest runner / can run through lldb and produce a junit XML artifact 2019-03-29 15:54:05 -07:00
Benjamin Sergeant
6cb2aaab65 Bump sleep time in test shell script 2019-03-29 09:36:56 -07:00
Benjamin Sergeant
c604c4591f (redis_subscribe) in verbose mode, received message gets printed with a 'received: ' header 2019-03-29 09:35:19 -07:00
Benjamin Sergeant
f0f54434cb Add clarification notice about third party modules 2019-03-29 09:34:17 -07:00
Benjamin Sergeant
f9de85c257 offline version of remark-latest 2019-03-28 16:06:43 -07:00
Benjamin Sergeant
44f817646e redis conf slides 2019-03-28 14:17:19 -07:00
Benjamin Sergeant
91786779f8 slides 2019-03-27 16:27:52 -07:00
Benjamin Sergeant
27d0aed2c9 add redis-conf slides 2019-03-27 15:53:55 -07:00
Benjamin Sergeant
7767c96a9e ws redis command improvements + test script 2019-03-27 13:41:46 -07:00
Benjamin Sergeant
3388bb50e1 (ws) redis_subscribe and redis_publish can take a password + display subscribe response 2019-03-26 09:33:22 -07:00
Benjamin Sergeant
1554c587b3 update doc 2019-03-24 21:48:14 -07:00
Benjamin Sergeant
ce70e73a34 remove Formula folder
Homebrew stuff is at https://github.com/bsergean/homebrew-IXWebSocket
2019-03-24 21:43:38 -07:00
Benjamin Sergeant
804ec9246f (server) fix masking bug 2019-03-22 15:33:04 -07:00
Benjamin Sergeant
f029321664 can send TEXT message (we only support BINARY messages now) 2019-03-22 14:24:22 -07:00
Benjamin Sergeant
d41b7f64e4 unmasked code is broken 2019-03-22 14:24:15 -07:00
Benjamin Sergeant
0366d1afb9 remove printf + unittest fix 2019-03-22 09:56:28 -07:00
Benjamin Sergeant
4ef3073ca4 (server) server should not mask data when sending to client (some python client libraries enforce that and assert) 2019-03-22 09:53:56 -07:00
Benjamin Sergeant
68a53aa884 (server) HTTP response is malformed 2019-03-22 09:52:19 -07:00
Benjamin Sergeant
2358b3ff26 minor cleanup 2019-03-21 13:51:25 -07:00
Benjamin Sergeant
00ed1d2817 HTTP/1.1 response should contains a reason (websocket server)
Fix compatibility problem with websockets python library, where the response does not contains a reason

File "/.../lib/python3.7/site-packages/websockets/http.py", line 126, in read_response
version, status_code, reason = status_line[:-2].split(b' ', 2)
ValueError: not enough values to unpack (expected 3, got 2)

The above exception was the direct cause of the following exception:

websockets.exceptions.InvalidMessage: Malformed HTTP message
2019-03-21 13:43:47 -07:00
Benjamin Sergeant
5b6fdb6526 cleanup, remove dead method 2019-03-21 10:06:59 -07:00
Benjamin Sergeant
fe700d1e7b (cmake) add a warning about 32/64 conversion problems. 2019-03-20 21:51:38 -07:00
Benjamin Sergeant
eac611ab1e Feature/connection state (#25)
* (cmake) add a warning about 32/64 conversion problems.

* fix typo

* New connection state for server code + fix OpenSSL double init bug

* update README
2019-03-20 18:34:24 -07:00
Benjamin Sergeant
0635313566 Feature/redis (#23)
* Fix warning

* (cmake) add a warning about 32/64 conversion problems.

* simple redis clients

* can publish to redis

* redis subscribe

* display messages received per second

* verbose flag

* (cmake) use clang only compile option -Wshorten-64-to-32 when compiling with clang
2019-03-20 14:29:02 -07:00
Michael Lu
663299c91e Fixed heartbeat typos (#22) 2019-03-19 21:31:43 -07:00
Benjamin Sergeant
523a6e989a make PollResultType an enum class 2019-03-19 09:29:57 -07:00
Benjamin Sergeant
13f4aee5ee fix bug with isReadyToWrite 2019-03-18 22:05:04 -07:00
Benjamin Sergeant
bbc0e2106c workaround bug in Socket::isReadyToWrite 2019-03-18 20:37:33 -07:00
Benjamin Sergeant
eb6ee52aaa use milliseconds 2019-03-18 20:17:44 -07:00
Benjamin Sergeant
80e330d4c3 ws / log subcommand name 2019-03-18 17:54:06 -07:00
Benjamin Sergeant
a3adc49d8c disable sigpipe on osx when writing/reading into a dead pipe 2019-03-18 17:52:01 -07:00
Benjamin Sergeant
9c6eeed0f8 edit homebrew install steps 2019-03-18 15:45:33 -07:00
Benjamin Sergeant
705e9a93f8 add an install target 2019-03-18 15:11:08 -07:00
Benjamin Sergeant
572a217050 no default parameters for isReadyToWrite and isReadyToRead 2019-03-18 14:31:21 -07:00
Benjamin Sergeant
d58798e36c when trying to flush the send buffer, use select to wait until it is possible instead of using sleep to retry at a given frequency 2019-03-18 14:25:27 -07:00
Dimon4eg
e98634a277 Fix typo (#19) 2019-03-17 16:08:28 -07:00
Benjamin Sergeant
f1f08eced0 remove unused gitmodule file 2019-03-17 10:38:48 -07:00
Benjamin Sergeant
6c2da9f0e4 remove unused folder 2019-03-17 10:38:19 -07:00
Benjamin Sergeant
e158635f57 linux hangs when closing 2019-03-16 11:38:23 -07:00
Benjamin Sergeant
5a241e77da edit README 2019-03-16 11:32:46 -07:00
Benjamin Sergeant
68e397ab34 use pipe to abort select on Linux as well as macOS 2019-03-15 17:46:40 -07:00
Benjamin Sergeant
4c78b94cd8 missing assert include on Linux 2019-03-15 11:43:27 -07:00
Benjamin Sergeant
3a9cc9b079 cleanup 2019-03-15 11:41:57 -07:00
Benjamin Sergeant
6ff8c6e7f3 unittest fix 2019-03-14 18:58:16 -07:00
Benjamin Sergeant
6f90425154 linux compile fix 2019-03-14 18:55:33 -07:00
Benjamin Sergeant
49ec9b1d9e linux fixes 2019-03-14 18:54:47 -07:00
Benjamin Sergeant
a0e35ad644 build fix 2019-03-14 18:53:21 -07:00
Benjamin Sergeant
b91dc77d6f select interrupt cleanup 2019-03-14 18:37:38 -07:00
Benjamin Sergeant
b462b5a5c8 linux build fix 2019-03-14 15:17:17 -07:00
Benjamin Sergeant
b5e7fb20b6 replace uint8_t with uint64_t for the send/close requests types / use named variable to index into the _fildes array 2019-03-14 15:03:57 -07:00
Benjamin Sergeant
9d245add9c set a default close reason string 2019-03-14 14:52:51 -07:00
Benjamin Sergeant
ded03ed743 do not busy loop while sending 2019-03-14 14:48:08 -07:00
Benjamin Sergeant
6cc260c04e remove docker folder 2019-03-14 14:48:02 -07:00
Benjamin Sergeant
5b4354a6f3 send optimization + ws file transfer test 2019-03-14 14:47:53 -07:00
Benjamin Sergeant
34de36fe01 Update README.md
Stop lying about Windows support ...
2019-03-13 23:10:40 -07:00
Benjamin Sergeant
08c2cdbf1d send can fail silently when sending would block (EWOULDBLOCK return for send) (#18)
* try to use a pipe for communication

* flush send buffer on the background thread

* cleanup

* linux fix / linux still use event fd for now

* cleanup
2019-03-13 23:09:45 -07:00
Benjamin Sergeant
dedbeb3eab websocket send: make sure all data in the kernel buffer is sent 2019-03-11 22:16:55 -07:00
Benjamin Sergeant
d88bf16500 add new message type when receiving message fragments 2019-03-11 11:12:43 -07:00
Benjamin Sergeant
ad9c8318a7 ws broacast_server / can set serving hostname 2019-03-10 16:36:44 -07:00
Benjamin Sergeant
f2778c0729 debian 9 unittest build fix 2019-03-10 16:07:48 -07:00
Benjamin Sergeant
03ca73658c asan test suite fix 2019-03-09 10:45:40 -08:00
Benjamin Sergeant
1da5f6c30c unittest + warning fix 2019-03-09 10:37:14 -08:00
Benjamin Sergeant
bee8a99a34 add ability to run with asan on macOS 2019-03-05 17:07:28 -08:00
Benjamin Sergeant
f5efd41dc1 fix compiler warnings in ws command line tool 2019-03-04 13:56:30 -08:00
Benjamin Sergeant
c202f8cf1d Socket code refactoring, plus stop polling with a 1s timeout in readBytes while we only want to poll with a 1ms timeout 2019-03-04 13:40:15 -08:00
Benjamin Sergeant
0c226c7629 readBytes does not read bytes one by one but in chunks 2019-03-02 21:11:16 -08:00
Benjamin Sergeant
a9e772f330 create a blocking + cancellable Socket::readBytes method 2019-03-02 15:16:46 -08:00
Benjamin Sergeant
86cc76388e create a blocking + cancellable Socket::readBytes method 2019-03-02 11:01:51 -08:00
Benjamin Sergeant
0f4e9af172 more ws doc 2019-02-28 22:07:45 -08:00
Benjamin Sergeant
3a1352c8ec more ws doc 2019-02-28 22:03:48 -08:00
Benjamin Sergeant
2c86fd947f Feature/http (#16)
* add skeleton and broken http client code.

GET returns "Resource temporarily unavailable" errors...

* linux compile fix

* can GET some pages

* Update formatting in README.md

* unittest for sending large messages

* document bug

* Feature/send large message (#14)

* introduce send fragment

* can pass a fin frame

* can send messages which are a perfect multiple of the chunk size

* set fin only for last fragment

* cleanup

* last fragment should be of type CONTINUATION

* Add simple send and receive programs

* speedups receiving + better way to wait for thing

* receive speedup by using linked list of chunks instead of large array

* document bug

* use chunks to receive data

* trailing spaces

* Update README.md

Add note about message fragmentation.

* Feature/ws cli (#15)

* New command line tool for transfering files / still very beta.

* add readme

* use cli11 for argument parsing

* json -> msgpack

* stop using base64 and use binary which can be stored in message pack

* add target for building with homebrew

* all CMakeLists are referenced by the top level one

* add ws_chat and ws_connect sub commands to ws

* cleanup

* add echo and broadcast server as ws sub-commands

* add gitignore

* comments

* ping pong added to ws

* mv cobra_publisher under ws folder

* Update README.md

* linux build fix

* linux build fix

* move http_client to a ws sub-command

* simple HTTP post support (urlencode parameters)

* can specify extra headers

* chunk encoding / simple redirect support / -I option

* follow redirects is optional

* make README vim markdown plugin friendly

* cleanup argument parsing + add socket creation factory

* add missing file

* http gzip compression

* cleanup

* doc

* Feature/send large message (#14)

* introduce send fragment

* can pass a fin frame

* can send messages which are a perfect multiple of the chunk size

* set fin only for last fragment

* cleanup

* last fragment should be of type CONTINUATION

* Add simple send and receive programs

* speedups receiving + better way to wait for thing

* receive speedup by using linked list of chunks instead of large array

* document bug

* use chunks to receive data

* trailing spaces
2019-02-28 21:54:03 -08:00
Benjamin Sergeant
927484a71f Update README.md 2019-02-22 21:53:29 -08:00
Benjamin Sergeant
88adb8f5ef mv cobra_publisher under ws folder 2019-02-22 21:51:03 -08:00
Benjamin Sergeant
98e7f5cb22 ping pong added to ws 2019-02-22 21:47:57 -08:00
Benjamin Sergeant
67e5957064 comments 2019-02-22 21:27:49 -08:00
Benjamin Sergeant
8c3473a91a add gitignore 2019-02-22 21:26:25 -08:00
Benjamin Sergeant
1c775cb759 add echo and broadcast server as ws sub-commands 2019-02-22 21:25:56 -08:00
Benjamin Sergeant
c4054d4984 cleanup 2019-02-22 20:51:22 -08:00
Benjamin Sergeant
76e2f9f3ac add ws_chat and ws_connect sub commands to ws 2019-02-22 20:49:26 -08:00
Benjamin Sergeant
41a40b8b9f all CMakeLists are referenced by the top level one 2019-02-21 22:21:29 -08:00
Benjamin Sergeant
6b8694bfbf add target for building with homebrew 2019-02-21 22:05:30 -08:00
Benjamin Sergeant
2d696b6806 Feature/ws cli (#15)
* New command line tool for transfering files / still very beta.

* add readme

* use cli11 for argument parsing

* json -> msgpack

* stop using base64 and use binary which can be stored in message pack
2019-02-21 21:24:53 -08:00
Benjamin Sergeant
709a5ec89a Update README.md
Add note about message fragmentation.
2019-02-21 14:08:27 -08:00
Benjamin Sergeant
932bb732e0 Feature/send large message (#14)
* introduce send fragment

* can pass a fin frame

* can send messages which are a perfect multiple of the chunk size

* set fin only for last fragment

* cleanup

* last fragment should be of type CONTINUATION

* Add simple send and receive programs

* speedups receiving + better way to wait for thing

* receive speedup by using linked list of chunks instead of large array

* document bug

* use chunks to receive data

* trailing spaces
2019-02-20 18:59:07 -08:00
Benjamin Sergeant
dd4e29542c document bug 2019-02-16 10:33:37 -08:00
Benjamin Sergeant
726e66ca66 unittest for sending large messages 2019-02-16 10:32:02 -08:00
Benjamin Sergeant
474fd70ec7 Update formatting in README.md 2019-02-05 23:04:45 -08:00
Benjamin Sergeant
5074dbc3c6 more conf in CI 2019-01-29 17:50:19 -08:00
Benjamin Sergeant
eb54e7f1ae get free port that can be used by non root users (> 1024) 2019-01-28 15:24:19 -08:00
Benjamin Sergeant
8983dd97a1 use dynamically generated port number to configure servers in unittest 2019-01-28 15:24:19 -08:00
Benjamin Sergeant
7eaea28970 Merge pull request #13 from machinezone/user/bsergeant/poll
User/bsergeant/poll
2019-01-27 10:47:38 -08:00
Benjamin Sergeant
907605c59c windows build fix 2019-01-27 10:46:02 -08:00
Benjamin Sergeant
58921592c8 constexpr to declare number of fds 2019-01-26 21:01:36 -08:00
Benjamin Sergeant
b9c49c38ed linux fix 2019-01-26 20:57:48 -08:00
Benjamin Sergeant
76c97027c8 remove shutdown call 2019-01-26 20:54:23 -08:00
Benjamin Sergeant
5db3620f49 rebase poll branch 2019-01-26 20:50:25 -08:00
Benjamin Sergeant
a2e6fa0b16 insensitive string compare when validating server connection header 2019-01-25 16:17:51 -08:00
Benjamin Sergeant
1d359f0fc4 Merge pull request #12 from machinezone/user/bsergeant/heart-beat
Add an optional heartbeat
2019-01-25 16:14:28 -08:00
Benjamin Sergeant
885d245afb heartbeat correct 2019-01-25 16:11:39 -08:00
Benjamin Sergeant
75d01c0c11 close server socket on exit 2019-01-24 21:16:32 -08:00
Benjamin Sergeant
ea219e3ddd unittest fix 2019-01-24 19:54:10 -08:00
Benjamin Sergeant
e9cd54b2f4 add an heartbeat test 2019-01-24 18:50:07 -08:00
Benjamin Sergeant
a8b6573f96 hearbeat 2019-01-24 12:42:49 -08:00
Benjamin Sergeant
4e158c8ba7 Update README.md 2019-01-15 09:36:43 -08:00
Benjamin Sergeant
121c84a2d1 check and validate the Connection: Upgrade header in client/server 2019-01-15 09:31:37 -08:00
Benjamin Sergeant
bfb76de9ab Merge pull request #10 from tonylin0826/master
Fix missing "Upgrade" header error
2019-01-15 09:22:11 -08:00
Tony Lin
2434605c06 Fix missing upgrade header error 2019-01-15 15:35:37 +08:00
Benjamin Sergeant
a0f15bfb56 C++14 + use make_unique and make_shared to make shared pointers 2019-01-11 21:25:06 -08:00
Benjamin Sergeant
7fabd14a63 add travis badge 2019-01-08 10:13:23 -08:00
Benjamin Sergeant
6b4d2aeb07 travis -> osx 2019-01-08 10:04:47 -08:00
Benjamin Sergeant
5ab61b46b5 Revert "Revert "try asan on Linux"" [Back to asan on Linux]
This reverts commit 02a704a8c713af0968e8b4b54f71d480f6b4c0f7.
2019-01-07 21:13:48 -08:00
Benjamin Sergeant
39c9691d70 travis-ci: try to use clang on Linux 2019-01-07 20:49:03 -08:00
Benjamin Sergeant
d00960b33f Revert "try asan on Linux"
This reverts commit dd2360ed7049e7be3cd9c739783ae744df62df2f.
2019-01-07 20:47:25 -08:00
Benjamin Sergeant
4a5cfac2ea try asan on Linux 2019-01-07 18:29:44 -08:00
Benjamin Sergeant
7e1d21239f build with osx on travis 2019-01-07 18:16:29 -08:00
Benjamin Sergeant
1a8b870a9e fix simple compile error in test/IXTest.h 2019-01-07 18:08:11 -08:00
Benjamin Sergeant
3e150db493 add a travis file for real 2019-01-07 18:05:55 -08:00
Benjamin Sergeant
1cf8b7e952 add a travis file 2019-01-07 18:04:28 -08:00
Benjamin Sergeant
ea75432f12 update test remote ws url 2019-01-07 11:28:53 -08:00
Benjamin Sergeant
85370dfd21 remove cmake sanitizer submodule 2019-01-07 11:26:23 -08:00
Benjamin Sergeant
8a0afef825 check select errors better 2019-01-07 11:18:00 -08:00
Benjamin Sergeant
76f196206b sanitizer cmake stuff 2019-01-06 18:54:16 -08:00
Benjamin Sergeant
bf3e8195f7 DNSLookup _id member does not need to be an atomic 2019-01-06 18:32:19 -08:00
Benjamin Sergeant
bce3071a12 DNSLookup: fix #8 2019-01-06 18:27:26 -08:00
Benjamin Sergeant
911f684e4d DNSLookup: copy hostname and port instead of accessing member 2019-01-06 18:17:12 -08:00
Benjamin Sergeant
49bc156a56 return false -> return -1 2019-01-06 18:10:39 -08:00
Benjamin Sergeant
791c3701d7 add new unittest 2019-01-06 15:14:13 -08:00
Benjamin Sergeant
372af54e46 remove dead file 2019-01-06 14:26:11 -08:00
Benjamin Sergeant
20c8953e5b remove openssl testing bits for apple build 2019-01-06 14:21:49 -08:00
Benjamin Sergeant
2f9ad54bae gcc linux compile fix 2019-01-06 12:12:39 -08:00
Benjamin Sergeant
57c22cddb8 make a class hierarchy for server code (IXWebSocketServer <- IXSocketServer) 2019-01-06 12:09:31 -08:00
Benjamin Sergeant
a5026849a3 add log 2019-01-05 21:16:13 -08:00
Benjamin Sergeant
60dc765178 unittest + compiler warnings 2019-01-05 21:10:08 -08:00
Benjamin Sergeant
5e1c150024 windows compile fix 2019-01-05 21:02:55 -08:00
Benjamin Sergeant
0fd06bb592 int -> ssize_t for socker recv and send 2019-01-05 20:53:50 -08:00
Benjamin Sergeant
9641c8cf49 header refactoring 2019-01-05 20:38:43 -08:00
Benjamin Sergeant
4ca31be4a2 windows connect (compile fix) 2019-01-05 17:35:50 -08:00
Benjamin Sergeant
667f18cbfe windows connect potential fix 2019-01-05 17:32:21 -08:00
Benjamin Sergeant
4df5050760 more accurate description of errors 2019-01-05 17:18:43 -08:00
Benjamin Sergeant
f50881a72f SocketTest / more debug info 2019-01-05 17:10:01 -08:00
Benjamin Sergeant
b80696af00 windows (compile) fix 2019-01-05 17:04:09 -08:00
Benjamin Sergeant
3cb2f6dcf7 windows fix 2019-01-05 17:02:39 -08:00
Benjamin Sergeant
b1e2c4ce72 test failure is not noticed 2019-01-05 16:30:22 -08:00
Benjamin Sergeant
89ff9dd5ac fix gcc warning 2019-01-05 16:26:11 -08:00
Benjamin Sergeant
8b95b173cd makefile tweak 2019-01-05 14:43:21 -08:00
Benjamin Sergeant
80a877ddab add dns lookup test 2019-01-05 14:40:17 -08:00
Benjamin Sergeant
e892b21872 openssl cleanup 2019-01-05 11:42:25 -08:00
Benjamin Sergeant
c344913ae8 Merge pull request #7 from bsergean/user/bsergeant/appveyor_first
unittest on appveyor
2019-01-04 17:29:23 -08:00
Benjamin Sergeant
3eef8fba27 unittest on appveyor 2019-01-04 17:28:13 -08:00
Benjamin Sergeant
d34e47f716 windows fixes 2019-01-04 15:23:57 -08:00
Benjamin Sergeant
9bfba28d01 Merge pull request #6 from machinezone/user/bsergeant/server
Add support for writing websocket servers (IXWebSocketServer)
2019-01-03 18:47:30 -08:00
Benjamin Sergeant
cc43357fb4 Socket::readLine works with arbitrary long lines 2019-01-03 18:47:01 -08:00
Benjamin Sergeant
bce5ef2dca timeout is configurable 2019-01-03 18:33:08 -08:00
Benjamin Sergeant
3021ac4b95 remove useless FIXME comment 2019-01-03 18:02:03 -08:00
Benjamin Sergeant
385e80d185 new doc 2019-01-03 18:00:48 -08:00
Benjamin Sergeant
bd1c8873d0 capture path/uri when connecting, and pass it back through callbacks in the openInfo member 2019-01-03 17:44:10 -08:00
Benjamin Sergeant
6ac3bdb94a implement a max connections (default = 32) settings 2019-01-03 17:05:44 -08:00
Benjamin Sergeant
e964a0a1f0 correct validation of the request (request line + headers) 2019-01-03 13:41:06 -08:00
Benjamin Sergeant
97255fbd62 cancellation refactoring 2019-01-03 12:53:44 -08:00
Benjamin Sergeant
d5041f64be rename test file 2019-01-02 21:59:06 -08:00
Benjamin Sergeant
64f649d1f9 split handshake code into its own files, so that Transport file is less massive 2019-01-02 20:07:54 -08:00
Benjamin Sergeant
097c7e5397 server unittest for validating client request / new timeout cancellation handling (need refactoring) 2019-01-02 16:08:32 -08:00
Benjamin Sergeant
c6adc00eac add new broadcast server example 2019-01-02 08:17:03 -08:00
Benjamin Sergeant
b1710bfa31 server per message deflate support 2019-01-02 08:12:29 -08:00
Benjamin Sergeant
0e52c42970 echo server example is a real echo server, not a broadcast server 2019-01-02 08:10:39 -08:00
Benjamin Sergeant
2b136b2981 refactoring + cancellation was buggy during http upgrade 2019-01-02 07:45:07 -08:00
Benjamin Sergeant
b95e5e36dc use select to detect new incoming connections 2019-01-01 22:21:07 -08:00
Benjamin Sergeant
1bc5bc7f1c add a way to run in blocking more, which is useful for server mode to have N*thread instead of 2N*thread for N connections 2019-01-01 21:25:15 -08:00
Benjamin Sergeant
946d7015a2 more named constants 2019-01-01 19:23:27 -08:00
Benjamin Sergeant
4adf5720f0 gitignore stuff 2019-01-01 17:14:31 -08:00
Benjamin Sergeant
973a3f03c3 linux fix + unittest works with Linux 2019-01-01 17:13:26 -08:00
Benjamin Sergeant
06177afd6a unittest starts a server 2019-01-01 16:34:05 -08:00
Benjamin Sergeant
e5937638d4 crash when server failed to start 2019-01-01 16:14:46 -08:00
Benjamin Sergeant
7c4f14f941 thread accepting connections can be cancelled/stopped externally 2019-01-01 16:11:27 -08:00
Benjamin Sergeant
ead54d6c37 listen job run in its own thread, non blocking 2019-01-01 14:52:14 -08:00
Benjamin Sergeant
b749f3c724 (nitpick) reformat 2019-01-01 14:29:57 -08:00
Benjamin Sergeant
d279aecb87 cleanup / remove printf, add mutex, remove hardcoded values, can pass in a binding host 2019-01-01 14:28:41 -08:00
Benjamin Sergeant
67de0fc8da use shared_ptr 2019-01-01 13:53:13 -08:00
Benjamin Sergeant
8ed2399517 unittest pass 2019-01-01 13:47:25 -08:00
Benjamin Sergeant
cf340011e2 record workers in a map instead of a vector 2018-12-31 14:52:59 -08:00
Benjamin Sergeant
ec2ad37860 add a print statement when the connection is closed / still need to terminate server thread 2018-12-31 12:47:42 -08:00
Benjamin Sergeant
3443e82812 more cleanup to propagate server connection error and let onOpen callback execute 2018-12-31 12:43:47 -08:00
Benjamin Sergeant
63138507d6 only bind to localhost 2018-12-31 11:48:49 -08:00
Benjamin Sergeant
b2eb07db14 server code has a callback that takes a websocket 2018-12-30 22:12:13 -08:00
Benjamin Sergeant
379a845166 cleanup / use a websocket instead of raw websockettransport 2018-12-30 22:00:49 -08:00
Benjamin Sergeant
266cf93584 can accept multiple connection / server can send data back to client 2018-12-30 21:16:05 -08:00
Benjamin Sergeant
0ee71e9a09 proof of concept server implementation 2018-12-29 23:15:27 -08:00
Benjamin Sergeant
ea07afcc0b can create a socket from a fd 2018-12-29 21:53:33 -08:00
Benjamin Sergeant
43cd6d34ca add simple unittest 2018-12-29 18:34:08 -08:00
Benjamin Sergeant
3b67032adb add missing src files (IXSetThreadName.{cpp,h}) ... 2018-12-23 14:19:30 -08:00
Benjamin Sergeant
2d46a0605b fix warning: field '_eventCallback' will be initialized after field '_publishMode' 2018-12-23 14:18:53 -08:00
Benjamin Sergeant
ba54664748 Fix warning: field '_done' will be initialized after field '_wait' [-Wreorder] _done(false), 2018-12-23 14:17:30 -08:00
Benjamin Sergeant
a79f4c10a1 set thread name / rename example 2018-12-23 14:14:38 -08:00
Benjamin Sergeant
bd04b28b9e async dns lookup fix 2018-12-14 17:49:42 -08:00
Benjamin Sergeant
cbadecab33 non blocking dns lookup 2018-12-14 16:28:17 -08:00
Benjamin Sergeant
8c079787f0 add cancellation support while connecting, to speed up WebSocket::stop 2018-12-09 17:56:20 -08:00
Benjamin Sergeant
62528e6a0b http upgrade and connections use non blocking sockets 2018-12-09 14:07:40 -08:00
Benjamin Sergeant
49bf8bd830 threading race condition fixes, detected by TSAN 2018-12-06 08:27:28 -08:00
Benjamin Sergeant
c64bc20bb5 cleanup 2018-11-14 15:52:28 -08:00
Benjamin Sergeant
54da891f79 per-message deflate compression fixes 2018-11-13 17:46:05 -08:00
Benjamin Sergeant
e847716076 move files around 2018-11-12 17:56:59 -08:00
Benjamin Sergeant
3a68bbd1b2 update readme / remove reference to missing compression support now that it is supported ... 2018-11-12 09:01:42 -08:00
Benjamin Sergeant
9cb1d03411 tweaks doc / license + send proper error code when closing the connecion 2018-11-12 09:00:55 -08:00
Benjamin Sergeant
4fed156b90 per message deflate support (with zlib) 2018-11-09 18:42:09 -08:00
Benjamin Sergeant
de8bcd36e8 (satori_publisher) better error handling 2018-11-07 14:54:44 -08:00
Benjamin Sergeant
135cfe3238 Add some example shell scripts to build on Linux 2018-11-07 12:33:33 -08:00
Benjamin Sergeant
6dbfe28427 Add new example folder for publishing events to satori, with a minimal satori sdk 2018-11-07 12:26:32 -08:00
Benjamin Sergeant
2b203c4616 Add missing files ... 2018-11-07 12:25:38 -08:00
Benjamin Sergeant
f12e655cf8 Add DockerFile + parse rsv1 field 2018-11-07 11:45:17 -08:00
Benjamin Sergeant
cf0045a483 stopping connection on Linux does not close the socket, which can create problem when re-starting the connection 2018-11-01 17:02:49 -07:00
Benjamin Sergeant
9c81eeace0 add stop and start directives to ws_connect + display close info 2018-10-31 10:27:17 -07:00
Benjamin Sergeant
5b333f91f6 update readme.md 2018-10-27 11:46:11 -07:00
Benjamin Sergeant
912d926260 Handle Sec-WebSocket-Accept correctly 2018-10-27 10:24:48 -07:00
Benjamin Sergeant
a8dfd640a7 capture an error code and a reason when the server closes the connection 2018-10-25 18:51:19 -07:00
Benjamin Sergeant
390044b716 add doc about ping/pong 2018-10-25 15:14:31 -07:00
Benjamin Sergeant
8ac36e6ee5 ping pong example: more error handling 2018-10-25 14:46:23 -07:00
Benjamin Sergeant
208c693088 ping / pong support / fix bug in dispatching received message type 2018-10-25 14:43:35 -07:00
Benjamin Sergeant
eae2f7d113 Better ping/pong support 2018-10-25 14:43:35 -07:00
Benjamin Sergeant
45f92115f9 New ws_connect example. Close to wscat node.js tool. 2018-10-25 14:43:35 -07:00
Benjamin Sergeant
42f3adc7a2 Update README.md 2018-10-08 21:50:55 -07:00
Benjamin Sergeant
71b40c6d6c Windows support (no TLS yet) 2018-10-08 21:44:54 -07:00
Benjamin Sergeant
af12089e7a make TLS support optional 2018-10-08 15:24:36 -07:00
Benjamin Sergeant
33677c4b2b move examples around 2018-10-08 15:24:36 -07:00
Benjamin Sergeant
376c8c2e00 Update IXWebSocket.h
Remove dead code
2018-10-07 15:49:07 -07:00
Benjamin Sergeant
8232e9e8ce Update README.md
Advanced usage -> API
2018-10-07 15:47:38 -07:00
Benjamin Sergeant
436bf8deb5 more ssl peer validation stuff 2018-10-05 18:45:44 -07:00
Benjamin Sergeant
c858a1c9e5 Update README.md 2018-10-05 14:35:09 -07:00
690 changed files with 39248 additions and 146924 deletions

47
.clang-format Normal file
View File

@ -0,0 +1,47 @@
# https://releases.llvm.org/7.0.0/tools/clang/docs/ClangFormatStyleOptions.html
---
Language: Cpp
BasedOnStyle: WebKit
AlignAfterOpenBracket: Align
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 100
ConstructorInitializerAllOnOneLineOrOnePerLine: false
Cpp11BracedListStyle: true
FixNamespaceComments: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^["<](stdafx|pch)\.h[">]$'
Priority: -1
- Regex: '^<Windows\.h>$'
Priority: 3
- Regex: '^<(WinIoCtl|winhttp|Shellapi)\.h>$'
Priority: 4
- Regex: '.*'
Priority: 2
IndentCaseLabels: true
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
PenaltyReturnTypeOnItsOwnLine: 1000
PointerAlignment: Left
SpaceAfterTemplateKeyword: false
SpaceAfterCStyleCast: true
Standard: Cpp11
UseTab: Never

View File

@ -1,3 +1,5 @@
build
CMakeCache.txt
ws/CMakeCache.txt
test/build
makefile

66
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: docker
# When its time to do a release do a build for amd64
# and push all of them to Docker Hub.
# Only trigger on semver shaped tags.
on:
push:
tags:
- "v*.*.*"
jobs:
login:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Prepare
id: prep
run: |
DOCKER_IMAGE=machinezone/ws
VERSION=edge
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
fi
if [ "${{ github.event_name }}" = "schedule" ]; then
VERSION=nightly
fi
TAGS="${DOCKER_IMAGE}:${VERSION}"
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=tags::${TAGS}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to GitHub Package Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2-build-push
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
target: prod
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.prep.outputs.tags }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache

30
.github/workflows/mkdocs.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: mkdocs
on:
push:
branches:
- master
paths:
- 'docs/**'
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mkdocs
pip install mkdocs-material
pip install pygments
- name: Build doc
run: |
git checkout master
git clean -dfx .
git fetch
git pull
mkdocs gh-deploy

15
.github/workflows/unittest_linux.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: linux
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: make test
run: make -f makefile.dev test

View File

@ -0,0 +1,15 @@
name: linux_asan
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: make test_asan
run: make -f makefile.dev test_asan

View File

@ -0,0 +1,17 @@
name: mac_tsan_mbedtls
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
mac_tsan_mbedtls:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: install mbedtls
run: brew install mbedtls
- name: make test
run: make -f makefile.dev test_tsan_mbedtls

View File

@ -0,0 +1,17 @@
name: mac_tsan_openssl
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
mac_tsan_openssl:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: install openssl
run: brew install openssl@1.1
- name: make test
run: make -f makefile.dev test_tsan_openssl

View File

@ -0,0 +1,15 @@
name: mac_tsan_sectransport
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
mac_tsan_sectransport:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: make test_tsan_sectransport
run: make -f makefile.dev test_tsan_sectransport

45
.github/workflows/unittest_uwp.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: uwp
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
uwp:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-vsdevenv@master
- uses: seanmiddleditch/gha-setup-ninja@master
- run: |
mkdir build
cd build
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 ..
- run: |
cd build
ninja
- run: |
cd build
ninja test
#
# 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

View File

@ -0,0 +1,28 @@
name: windows_gcc
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: bsergean/setup-mingw@d79ce405bac9edef3a1726ef00554a56f0bafe66
- run: |
mkdir build
cd build
cmake -GNinja -DCMAKE_CXX_COMPILER=c++ -DCMAKE_C_COMPILER=cc -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 -DCMAKE_UNITY_BUILD=ON ..
- run: |
cd build
ninja
- run: |
cd build
ctest -V
# ninja test
#- run: ../build/test/ixwebsocket_unittest.exe
# working-directory: test

10
.gitignore vendored
View File

@ -1,2 +1,12 @@
build
*.pyc
venv
ixsnake/ixsnake/.certs/
site/
ws/.certs/
ws/.srl
ixhttpd
makefile
a.out
.idea/
cmake-build-debug/

12
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,12 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pocc/pre-commit-hooks
rev: v1.1.1
hooks:
- id: clang-format
args: [-i, -style=file]

View File

@ -1,17 +0,0 @@
language: cpp
dist: xenial
compiler:
- gcc
- clang
os:
- linux
- osx
matrix:
exclude:
# GCC fails on recent Travis OSX images.
- compiler: gcc
os: osx
script: python test/run.py

19
CMake/FindDeflate.cmake Normal file
View File

@ -0,0 +1,19 @@
# Find package structure taken from libcurl
include(FindPackageHandleStandardArgs)
find_path(DEFLATE_INCLUDE_DIRS libdeflate.h)
find_library(DEFLATE_LIBRARY deflate)
find_package_handle_standard_args(Deflate
FOUND_VAR
DEFLATE_FOUND
REQUIRED_VARS
DEFLATE_LIBRARY
DEFLATE_INCLUDE_DIRS
FAIL_MESSAGE
"Could NOT find deflate"
)
set(DEFLATE_INCLUDE_DIRS ${DEFLATE_INCLUDE_DIRS})
set(DEFLATE_LIBRARIES ${DEFLATE_LIBRARY})

16
CMake/FindMbedTLS.cmake Normal file
View File

@ -0,0 +1,16 @@
find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h)
# mbedtls-3.0 changed headers files, and we need to ifdef'out a few things
find_path(MBEDTLS_VERSION_GREATER_THAN_3 mbedtls/build_info.h)
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MbedTLS DEFAULT_MSG
MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)

19
CMake/FindSpdLog.cmake Normal file
View File

@ -0,0 +1,19 @@
# Find package structure taken from libcurl
include(FindPackageHandleStandardArgs)
find_path(SPDLOG_INCLUDE_DIRS spdlog/spdlog.h)
find_library(JSONCPP_LIBRARY spdlog)
find_package_handle_standard_args(SPDLOG
FOUND_VAR
SPDLOG_FOUND
REQUIRED_VARS
SPDLOG_LIBRARY
SPDLOG_INCLUDE_DIRS
FAIL_MESSAGE
"Could NOT find spdlog"
)
set(SPDLOG_INCLUDE_DIRS ${SPDLOG_INCLUDE_DIRS})
set(SPDLOG_LIBRARIES ${SPDLOG_LIBRARY})

View File

@ -3,15 +3,23 @@
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required(VERSION 3.4.1)
project(ixwebsocket C CXX)
cmake_minimum_required(VERSION 3.4.1...3.17.2)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
set (CMAKE_CXX_STANDARD 14)
project(ixwebsocket LANGUAGES C CXX VERSION 11.4.6)
set (CMAKE_CXX_STANDARD 11)
set (CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
set (CMAKE_EXPORT_COMPILE_COMMANDS yes)
# -Wshorten-64-to-32 does not work with clang
if (NOT WIN32)
option (BUILD_DEMO OFF)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
endif()
@ -20,125 +28,238 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
endif()
set( IXWEBSOCKET_SOURCES
ixwebsocket/IXBench.cpp
ixwebsocket/IXCancellationRequest.cpp
ixwebsocket/IXConnectionState.cpp
ixwebsocket/IXDNSLookup.cpp
ixwebsocket/IXExponentialBackoff.cpp
ixwebsocket/IXGetFreePort.cpp
ixwebsocket/IXGzipCodec.cpp
ixwebsocket/IXHttp.cpp
ixwebsocket/IXHttpClient.cpp
ixwebsocket/IXHttpServer.cpp
ixwebsocket/IXNetSystem.cpp
ixwebsocket/IXSelectInterrupt.cpp
ixwebsocket/IXSelectInterruptFactory.cpp
ixwebsocket/IXSelectInterruptPipe.cpp
ixwebsocket/IXSelectInterruptEvent.cpp
ixwebsocket/IXSetThreadName.cpp
ixwebsocket/IXSocket.cpp
ixwebsocket/IXSocketServer.cpp
ixwebsocket/IXSocketConnect.cpp
ixwebsocket/IXSocketFactory.cpp
ixwebsocket/IXDNSLookup.cpp
ixwebsocket/IXCancellationRequest.cpp
ixwebsocket/IXNetSystem.cpp
ixwebsocket/IXSocketServer.cpp
ixwebsocket/IXSocketTLSOptions.cpp
ixwebsocket/IXStrCaseCompare.cpp
ixwebsocket/IXUdpSocket.cpp
ixwebsocket/IXUrlParser.cpp
ixwebsocket/IXUuid.cpp
ixwebsocket/IXUserAgent.cpp
ixwebsocket/IXWebSocket.cpp
ixwebsocket/IXWebSocketServer.cpp
ixwebsocket/IXWebSocketTransport.cpp
ixwebsocket/IXWebSocketCloseConstants.cpp
ixwebsocket/IXWebSocketHandshake.cpp
ixwebsocket/IXWebSocketHttpHeaders.cpp
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
ixwebsocket/IXWebSocketHttpHeaders.cpp
ixwebsocket/IXHttpClient.cpp
ixwebsocket/IXUrlParser.cpp
ixwebsocket/LUrlParser.cpp
ixwebsocket/IXSelectInterrupt.cpp
ixwebsocket/IXSelectInterruptFactory.cpp
ixwebsocket/IXConnectionState.cpp
ixwebsocket/IXWebSocketProxyServer.cpp
ixwebsocket/IXWebSocketServer.cpp
ixwebsocket/IXWebSocketTransport.cpp
)
set( IXWEBSOCKET_HEADERS
ixwebsocket/IXSocket.h
ixwebsocket/IXSocketServer.h
ixwebsocket/IXSocketConnect.h
ixwebsocket/IXSocketFactory.h
ixwebsocket/IXSetThreadName.h
ixwebsocket/IXDNSLookup.h
ixwebsocket/IXBase64.h
ixwebsocket/IXBench.h
ixwebsocket/IXCancellationRequest.h
ixwebsocket/IXConnectionState.h
ixwebsocket/IXDNSLookup.h
ixwebsocket/IXExponentialBackoff.h
ixwebsocket/IXGetFreePort.h
ixwebsocket/IXGzipCodec.h
ixwebsocket/IXHttp.h
ixwebsocket/IXHttpClient.h
ixwebsocket/IXHttpServer.h
ixwebsocket/IXNetSystem.h
ixwebsocket/IXProgressCallback.h
ixwebsocket/IXSelectInterrupt.h
ixwebsocket/IXSelectInterruptFactory.h
ixwebsocket/IXSelectInterruptPipe.h
ixwebsocket/IXSelectInterruptEvent.h
ixwebsocket/IXSetThreadName.h
ixwebsocket/IXSocket.h
ixwebsocket/IXSocketConnect.h
ixwebsocket/IXSocketFactory.h
ixwebsocket/IXSocketServer.h
ixwebsocket/IXSocketTLSOptions.h
ixwebsocket/IXStrCaseCompare.h
ixwebsocket/IXUdpSocket.h
ixwebsocket/IXUniquePtr.h
ixwebsocket/IXUrlParser.h
ixwebsocket/IXUuid.h
ixwebsocket/IXUtf8Validator.h
ixwebsocket/IXUserAgent.h
ixwebsocket/IXWebSocket.h
ixwebsocket/IXWebSocketServer.h
ixwebsocket/IXWebSocketTransport.h
ixwebsocket/IXWebSocketHandshake.h
ixwebsocket/IXWebSocketSendInfo.h
ixwebsocket/IXWebSocketCloseConstants.h
ixwebsocket/IXWebSocketCloseInfo.h
ixwebsocket/IXWebSocketErrorInfo.h
ixwebsocket/IXWebSocketHandshake.h
ixwebsocket/IXWebSocketHandshakeKeyGen.h
ixwebsocket/IXWebSocketHttpHeaders.h
ixwebsocket/IXWebSocketInitResult.h
ixwebsocket/IXWebSocketMessage.h
ixwebsocket/IXWebSocketMessageType.h
ixwebsocket/IXWebSocketOpenInfo.h
ixwebsocket/IXWebSocketPerMessageDeflate.h
ixwebsocket/IXWebSocketPerMessageDeflateCodec.h
ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
ixwebsocket/IXWebSocketHttpHeaders.h
ixwebsocket/libwshandshake.hpp
ixwebsocket/IXHttpClient.h
ixwebsocket/IXUrlParser.h
ixwebsocket/LUrlParser.h
ixwebsocket/IXSelectInterrupt.h
ixwebsocket/IXSelectInterruptFactory.h
ixwebsocket/IXConnectionState.h
ixwebsocket/IXWebSocketProxyServer.h
ixwebsocket/IXWebSocketSendData.h
ixwebsocket/IXWebSocketSendInfo.h
ixwebsocket/IXWebSocketServer.h
ixwebsocket/IXWebSocketTransport.h
ixwebsocket/IXWebSocketVersion.h
)
if (UNIX)
# Linux, Mac, iOS, Android
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.cpp )
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.h )
endif()
option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF)
option(USE_TLS "Enable TLS support" FALSE)
# Platform specific code
if (APPLE)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/apple/IXSetThreadName_apple.cpp)
elseif (WIN32)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/windows/IXSetThreadName_windows.cpp)
else()
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptEventFd.cpp)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSelectInterruptEventFd.h)
endif()
set(USE_OPEN_SSL FALSE)
if (USE_TLS)
add_definitions(-DIXWEBSOCKET_USE_TLS)
# default to securetranport on Apple if nothing is configured
if (APPLE)
if (NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) # unless we want something else
set(USE_SECURE_TRANSPORT ON)
endif()
# default to mbedtls on windows if nothing is configured
elseif (WIN32)
if (NOT USE_OPEN_SSL) # unless we want something else
set(USE_MBED_TLS ON)
endif()
else() # default to OpenSSL on all other platforms
if (NOT USE_MBED_TLS) # Unless mbedtls is requested
set(USE_OPEN_SSL ON)
set(requires "openssl")
endif()
endif()
if (USE_MBED_TLS)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketMbedTLS.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketMbedTLS.cpp)
elseif (USE_SECURE_TRANSPORT)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketAppleSSL.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketAppleSSL.cpp)
elseif (WIN32)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketSChannel.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketSChannel.cpp)
else()
set(USE_OPEN_SSL TRUE)
elseif (USE_OPEN_SSL)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketOpenSSL.cpp)
else()
message(FATAL_ERROR "TLS Configuration error: unknown backend")
endif()
endif()
add_library( ixwebsocket STATIC
${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS}
)
if(BUILD_SHARED_LIBS)
# Building shared library
if(MSVC)
# Workaround for some projects
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
if (APPLE AND USE_TLS)
target_link_libraries(ixwebsocket "-framework foundation" "-framework security")
add_library( ixwebsocket SHARED
${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS}
)
# Set library version
set_target_properties(ixwebsocket PROPERTIES VERSION ${PROJECT_VERSION})
else()
# Static library
add_library( ixwebsocket
${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS}
)
endif()
if (USE_OPEN_SSL)
find_package(OpenSSL REQUIRED)
add_definitions(${OPENSSL_DEFINITIONS})
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
include_directories(${OPENSSL_INCLUDE_DIR})
target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES})
if (USE_TLS)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_TLS)
if (USE_MBED_TLS)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_MBED_TLS)
elseif (USE_OPEN_SSL)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_OPEN_SSL)
elseif (USE_SECURE_TRANSPORT)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_SECURE_TRANSPORT)
else()
message(FATAL_ERROR "TLS Configuration error: unknown backend")
endif()
endif()
if (USE_TLS)
if (USE_OPEN_SSL)
message(STATUS "TLS configured to use openssl")
# Help finding Homebrew's OpenSSL on macOS
if (APPLE)
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib)
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include)
# This is for MacPort OpenSSL 1.0
# set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib/openssl-1.0)
# set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0)
endif()
# This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
target_link_libraries(ixwebsocket PRIVATE ${OPENSSL_LIBRARIES})
elseif (USE_MBED_TLS)
message(STATUS "TLS configured to use mbedtls")
# This MBEDTLS_FOUND check is to help find a cmake manually configured MbedTLS
if (NOT MBEDTLS_FOUND)
find_package(MbedTLS REQUIRED)
if (MBEDTLS_VERSION_GREATER_THAN_3)
target_compile_definitions(ixwebsocket PRIVATE IXWEBSOCKET_USE_MBED_TLS_MIN_VERSION_3)
endif()
endif()
target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${MBEDTLS_INCLUDE_DIRS}>)
target_link_libraries(ixwebsocket PRIVATE ${MBEDTLS_LIBRARIES})
elseif (USE_SECURE_TRANSPORT)
message(STATUS "TLS configured to use secure transport")
target_link_libraries(ixwebsocket PRIVATE "-framework Foundation" "-framework Security")
endif()
endif()
option(USE_ZLIB "Enable zlib support" TRUE)
if (USE_ZLIB)
find_package(ZLIB REQUIRED)
target_link_libraries(ixwebsocket PRIVATE ZLIB::ZLIB)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
endif()
if (WIN32)
add_subdirectory(third_party/zlib)
include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib)
target_link_libraries(ixwebsocket zlibstatic wsock32 ws2_32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
target_link_libraries(ixwebsocket PRIVATE wsock32 ws2_32 shlwapi)
target_compile_definitions(ixwebsocket PRIVATE _CRT_SECURE_NO_WARNINGS)
else()
# gcc/Linux needs -pthread
find_package(Threads)
target_link_libraries(ixwebsocket
z ${CMAKE_THREAD_LIBS_INIT})
if (USE_TLS)
target_link_libraries(ixwebsocket PRIVATE Crypt32)
endif()
endif()
if (UNIX AND NOT APPLE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads)
target_link_libraries(ixwebsocket PRIVATE Threads::Threads)
endif()
set( IXWEBSOCKET_INCLUDE_DIRS
.
${CMAKE_CURRENT_SOURCE_DIR}
)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
@ -146,15 +267,59 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(ixwebsocket PRIVATE /MP)
endif()
target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} )
include(GNUInstallDirs)
target_include_directories(ixwebsocket PUBLIC
$<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket>
)
set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}")
install(TARGETS ixwebsocket
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/
)
add_library(ixwebsocket::ixwebsocket ALIAS ixwebsocket)
if (NOT WIN32)
add_subdirectory(ws)
option(IXWEBSOCKET_INSTALL "Install IXWebSocket" TRUE)
if (IXWEBSOCKET_INSTALL)
install(TARGETS ixwebsocket
EXPORT ixwebsocket
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/
)
configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket")
set(prefix ${CMAKE_INSTALL_PREFIX})
configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
install(EXPORT ixwebsocket
FILE ixwebsocket-targets.cmake
NAMESPACE ixwebsocket::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket
)
endif()
if (USE_WS OR USE_TEST)
include(FetchContent)
FetchContent_Declare(spdlog
GIT_REPOSITORY "https://github.com/gabime/spdlog"
GIT_TAG "v1.8.0"
GIT_SHALLOW 1)
FetchContent_MakeAvailable(spdlog)
if (USE_WS)
add_subdirectory(ws)
endif()
if (USE_TEST)
enable_testing()
add_subdirectory(test)
endif()
endif()
if (BUILD_DEMO)
add_executable(demo main.cpp)
target_link_libraries(demo ixwebsocket)
endif()

View File

@ -1 +0,0 @@
2.0.0

View File

@ -1 +1 @@
docker/Dockerfile.ubuntu_xenial
docker/Dockerfile.alpine

522
README.md
View File

@ -1,422 +1,152 @@
# General
## Hello world
![Alt text](https://travis-ci.org/machinezone/IXWebSocket.svg?branch=master)
(note from the main developer, sadly I don't have too much time to devote to this library anymore, maybe it's time to pass the maintenance to someone else more motivated ?)
## Introduction
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex and bi-directionnal communication channels over a single TCP connection. *IXWebSocket* is a C++ library for client and server Websocket communication, and for client HTTP communication. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient) and from the [Satori C SDK](https://github.com/satori-com/satori-rtm-sdk-c). It has been tested on the following platforms.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
* macOS
* iOS
* Linux
* Android
* Windows (no TLS)
```cpp
/*
* main.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*
* Super simple standalone example. See ws folder, unittest and doc/usage.md for more.
*
* On macOS
* $ mkdir -p build ; (cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install)
* $ clang++ --std=c++11 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
* $ ./a.out
*
* Or use cmake -DBUILD_DEMO=ON option for other platforms
*/
## Examples
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXUserAgent.h>
#include <iostream>
The [*ws*](https://github.com/machinezone/IXWebSocket/tree/master/ws) folder countains many interactive programs for chat, [file transfers](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_send.cpp), [curl like](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_http_client.cpp) http clients, demonstrating client and server usage.
Here is what the client API looks like.
```
ix::WebSocket webSocket;
std::string url("ws://localhost:8080/");
webSocket.setUrl(url);
// Optional heart beat, sent every 45 seconds when there is not any traffic
// to make sure that load balancers do not kill an idle connection.
webSocket.setHeartBeatPeriod(45);
// Setup a callback to be fired when a message or an event (open, close, error) is received
webSocket.setOnMessageCallback(
[](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocketMessageType::Message)
{
std::cout << str << std::endl;
}
});
// Now that our callback is setup, we can start our background thread and receive messages
webSocket.start();
// Send a message to the server (default to BINARY mode)
webSocket.send("hello world");
// The message can be sent in TEXT mode
webSocket.sendText("hello again");
// ... finally ...
// Stop the connection
webSocket.stop()
```
Here is what the server API looks like. Note that server support is very recent and subject to changes.
```
// Run a server on localhost at a given port.
// Bound host name, max connections and listen backlog can also be passed in as parameters.
ix::WebSocketServer server(port);
server.setOnConnectionCallback(
[&server](std::shared_ptr<WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState)
{
webSocket->setOnMessageCallback(
[webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocketMessageType::Open)
{
std::cerr << "New connection" << std::endl;
// A connection state object is available, and has a default id
// You can subclass ConnectionState and pass an alternate factory
// to override it. It is useful if you want to store custom
// attributes per connection (authenticated bool flag, attributes, etc...)
std::cerr << "id: " << connectionState->getId() << std::endl;
// The uri the client did connect to.
std::cerr << "Uri: " << openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : openInfo.headers)
{
std::cerr << it.first << ": " << it.second << std::endl;
}
}
else if (messageType == ix::WebSocketMessageType::Message)
{
// For an echo server, we just send back to the client whatever was received by the server
// All connected clients are available in an std::set. See the broadcast cpp example.
webSocket->send(str);
}
}
);
}
);
auto res = server.listen();
if (!res.first)
int main()
{
// Error handling
return 1;
// Required on Windows
ix::initNetSystem();
// Our websocket object
ix::WebSocket webSocket;
// Connect to a server with encryption
// See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration
// https://github.com/machinezone/IXWebSocket/issues/386#issuecomment-1105235227 (self signed certificates)
std::string url("wss://echo.websocket.org");
webSocket.setUrl(url);
std::cout << "Connecting to " << url << "..." << std::endl;
// 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([](const ix::WebSocketMessagePtr& msg)
{
if (msg->type == ix::WebSocketMessageType::Message)
{
std::cout << "received message: " << msg->str << std::endl;
std::cout << "> " << std::flush;
}
else if (msg->type == ix::WebSocketMessageType::Open)
{
std::cout << "Connection established" << std::endl;
std::cout << "> " << std::flush;
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
// Maybe SSL is not configured properly
std::cout << "Connection error: " << msg->errorInfo.reason << std::endl;
std::cout << "> " << std::flush;
}
}
);
// Now that our callback is setup, we can start our background thread and receive messages
webSocket.start();
// Send a message to the server (default to TEXT mode)
webSocket.send("hello world");
// Display a prompt
std::cout << "> " << std::flush;
std::string text;
// Read text from the console and send messages in text mode.
// Exit with Ctrl-D on Unix or Ctrl-Z on Windows.
while (std::getline(std::cin, text))
{
webSocket.send(text);
std::cout << "> " << std::flush;
}
return 0;
}
// Run the server in the background. Server can be stoped by calling server.stop()
server.start();
// Block until server.stop() is called.
server.wait();
```
Here is what the HTTP client API looks like. Note that HTTP client support is very recent and subject to changes.
Interested? Go read the [docs](https://machinezone.github.io/IXWebSocket/)! If things don't work as expected, please create an issue on GitHub, or even better a pull request if you know how to fix your problem.
```
//
// Preparation
//
HttpClient httpClient;
HttpRequestArgs args;
IXWebSocket is actively being developed, check out the [changelog](https://machinezone.github.io/IXWebSocket/CHANGELOG/) to know what's cooking. If you are looking for a real time messaging service (the chat-like 'server' your websocket code will talk to) with many features such as history, backed by Redis, look at [cobra](https://github.com/machinezone/cobra).
// Custom headers can be set
WebSocketHttpHeaders headers;
headers["Foo"] = "bar";
args.extraHeaders = headers;
IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code.
// Timeout options
args.connectTimeout = connectTimeout;
args.transferTimeout = transferTimeout;
Starting with the 11.0.8 release, IXWebSocket should be fully C++11 compatible.
// Redirect options
args.followRedirects = followRedirects;
args.maxRedirects = maxRedirects;
## Users
// Misc
args.compress = compress; // Enable gzip compression
args.verbose = verbose;
args.logger = [](const std::string& msg)
{
std::cout << msg;
};
If your company or project is using this library, feel free to open an issue or PR to amend this list.
//
// Request
//
HttpResponse out;
std::string url = "https://www.google.com";
- [Machine Zone](https://www.mz.com)
- [Tokio](https://github.com/liz3/tokio), a discord library focused on audio playback with node bindings.
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework.
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod
- [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper (archived as of Oct 8, 2021)
- [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots
- [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar
- [Abaddon](https://github.com/uowuo/abaddon), An alternative Discord client made with C++/gtkmm
- [NovaCoin](https://github.com/novacoin-project/novacoin), a hybrid scrypt PoW + PoS based cryptocurrency.
- [Candy](https://github.com/lanthora/candy), A WebSocket and TUN based VPN for Linux
- [ITGmania](https://github.com/itgmania/itgmania), a cross platform Dance Dance Revolution-like emulator.
// HEAD request
out = httpClient.head(url, args);
## Alternative libraries
// GET request
out = httpClient.get(url, args);
There are plenty of great websocket libraries out there, which might work for you. Here are a couple of serious ones.
// POST request with parameters
HttpParameters httpParameters;
httpParameters["foo"] = "bar";
out = httpClient.post(url, httpParameters, args);
* [websocketpp](https://github.com/zaphoyd/websocketpp) - C++
* [beast](https://github.com/boostorg/beast) - C++
* [µWebSockets](https://github.com/uNetworking/uWebSockets) - C++
* [libwebsockets](https://libwebsockets.org/) - C
* [wslay](https://github.com/tatsuhiro-t/wslay) - C
// POST request with a body
out = httpClient.post(url, std::string("foo=bar"), args);
[uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv.
//
// Result
//
auto statusCode = std::get<0>(out);
auto errorCode = std::get<1>(out);
auto responseHeaders = std::get<2>(out);
auto payload = std::get<3>(out);
auto errorMsg = std::get<4>(out);
auto uploadSize = std::get<5>(out);
auto downloadSize = std::get<6>(out);
```
To check the performance of a websocket library, you can look at the [autoroute](https://github.com/bsergean/autoroute) project.
## Build
## Continuous Integration
CMakefiles for the library and the examples are available. This library has few dependencies, so it is possible to just add the source files into your project. Otherwise the usual way will suffice.
| OS | TLS | Sanitizer | Status |
|-------------------|-------------------|-------------------|-------------------|
| Linux | OpenSSL | None | [![Build2][1]][0] |
| macOS | Secure Transport | Thread Sanitizer | [![Build2][2]][0] |
| macOS | OpenSSL | Thread Sanitizer | [![Build2][3]][0] |
| macOS | MbedTLS | Thread Sanitizer | [![Build2][4]][0] |
| Windows | Disabled | None | [![Build2][5]][0] |
| UWP | Disabled | None | [![Build2][6]][0] |
| Linux | OpenSSL | Address Sanitizer | [![Build2][7]][0] |
```
mkdir build # make a build dir so that you can build out of tree.
cd build
cmake ..
make -j
make install # will install to /usr/local on Unix, on macOS it is a good idea to sudo chown -R `whoami`:staff /usr/local
```
* Some tests are disabled on Windows/UWP because of a pathing problem
* TLS and ZLIB are disabled on Windows/UWP because enabling make the CI run takes a lot of time, for setting up vcpkg.
Headers and a static library will be installed to the target dir.
[0]: https://github.com/machinezone/IXWebSocket
[1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg
[2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg
[3]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_openssl/badge.svg
[4]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_mbedtls/badge.svg
[5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg
[6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg
[7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg
A [conan](https://conan.io/) file is available at [conan-IXWebSocket](https://github.com/Zinnion/conan-IXWebSocket).
There is a unittest which can be executed by typing `make test`.
There is a Dockerfile for running some code on Linux. To use docker-compose you must make a docker container first.
```
$ make docker
...
$ docker compose up &
...
$ docker exec -it ixwebsocket_ws_1 bash
app@ca2340eb9106:~$ ws --help
ws is a websocket tool
...
```
Finally you can build and install the `ws command line tool` with Homebrew. The homebrew version might be slightly out of date.
```
brew tap bsergean/IXWebSocket
brew install IXWebSocket
```
## Implementation details
### Per Message Deflate compression.
The per message deflate compression option is supported. It can lead to very nice bandbwith savings (20x !) if your messages are similar, which is often the case for example for chat applications. All features of the spec should be supported.
### TLS/SSL
Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and macOS, and OpenSSL is used on Android and Linux.
### Polling and background thread work
No manual polling to fetch data is required. Data is sent and received instantly by using a background thread for receiving data and the select [system](http://man7.org/linux/man-pages/man2/select.2.html) call to be notified by the OS of incoming data. No timeout is used for select so that the background thread is only woken up when data is available, to optimize battery life. This is also the recommended way of using select according to the select tutorial, section [select law](https://linux.die.net/man/2/select_tut). Read and Writes to the socket are non blocking. Data is sent right away and not enqueued by writing directly to the socket, which is [possible](https://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid) since system socket implementations allow concurrent read/writes. However concurrent writes need to be protected with mutex.
### Automatic reconnection
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds.
### Large messages
Large frames are broken up into smaller chunks or messages to avoid filling up the os tcp buffers, which is permitted thanks to WebSocket [fragmentation](https://tools.ietf.org/html/rfc6455#section-5.4). Messages up to 1G were sent and received succesfully.
## Limitations
* No utf-8 validation is made when sending TEXT message with sendText()
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.
## C++ code organization
Here is a simplistic diagram which explains how the code is structured in term of class/modules.
```
+-----------------------+ --- Public
| | Start the receiving Background thread. Auto reconnection. Simple websocket Ping.
| IXWebSocket | Interface used by C++ test clients. No IX dependencies.
| |
+-----------------------+
| |
| IXWebSocketServer | Run a server and give each connections its own WebSocket object.
| | Each connection is handled in a new OS thread.
| |
+-----------------------+ --- Private
| |
| IXWebSocketTransport | Low level websocket code, framing, managing raw socket. Adapted from easywsclient.
| |
+-----------------------+
| |
| IXWebSocketHandshake | Establish the connection between client and server.
| |
+-----------------------+
| |
| IXWebSocket | ws:// Unencrypted Socket handler
| IXWebSocketAppleSSL | wss:// TLS encrypted Socket AppleSSL handler. Used on iOS and macOS
| IXWebSocketOpenSSL | wss:// TLS encrypted Socket OpenSSL handler. Used on Android and Linux
| | Can be used on macOS too.
+-----------------------+
| |
| IXSocketConnect | Connect to the remote host (client).
| |
+-----------------------+
| |
| IXDNSLookup | Does DNS resolution asynchronously so that it can be interrupted.
| |
+-----------------------+
```
## API
### Sending messages
`websocket.send("foo")` will send a message.
If the connection was closed and sending failed, the return value will be set to false.
### ReadyState
`getReadyState()` returns the state of the connection. There are 4 possible states.
1. ReadyState::Connecting - The connection is not yet open.
2. ReadyState::Open - The connection is open and ready to communicate.
3. ReadyState::Closing - The connection is in the process of closing.
4. ReadyState::Closed - The connection is closed or could not be opened.
### Open and Close notifications
The onMessage event will be fired when the connection is opened or closed. This is similar to the [Javascript browser API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket), which has `open` and `close` events notification that can be registered with the browser `addEventListener`.
```
webSocket.setOnMessageCallback(
[](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocketMessageType::Open)
{
std::cout << "send greetings" << std::endl;
// Headers can be inspected (pairs of string/string)
std::cout << "Handshake Headers:" << std::endl;
for (auto it : headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
}
else if (messageType == ix::WebSocketMessageType::Close)
{
std::cout << "disconnected" << std::endl;
// The server can send an explicit code and reason for closing.
// This data can be accessed through the closeInfo object.
std::cout << closeInfo.code << std::endl;
std::cout << closeInfo.reason << std::endl;
}
}
);
```
### Error notification
A message will be fired when there is an error with the connection. The message type will be `ix::WebSocketMessageType::Error`. Multiple fields will be available on the event to describe the error.
```
webSocket.setOnMessageCallback(
[](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Error: " << error.reason << std::endl;
ss << "#retries: " << event.retries << std::endl;
ss << "Wait time(ms): " << event.wait_time << std::endl;
ss << "HTTP Status: " << event.http_status << std::endl;
std::cout << ss.str() << std::endl;
}
}
);
```
### start, stop
1. `websocket.start()` connect to the remote server and starts the message receiving background thread.
2. `websocket.stop()` disconnect from the remote server and closes the background thread.
### Configuring the remote url
The url can be set and queried after a websocket object has been created. You will have to call `stop` and `start` if you want to disconnect and connect to that new url.
```
std::string url("wss://example.com");
websocket.configure(url);
```
### Ping/Pong support
Ping/pong messages are used to implement keep-alive. 2 message types exists to identify ping and pong messages. Note that when a ping message is received, a pong is instantly send back as requested by the WebSocket spec.
```
webSocket.setOnMessageCallback(
[](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocketMessageType::Ping ||
messageType == ix::WebSocketMessageType::Pong)
{
std::cout << "pong data: " << str << std::endl;
}
}
);
```
A ping message can be sent to the server, with an optional data string.
```
websocket.ping("ping data, optional (empty string is ok): limited to 125 bytes long");
```
### Heartbeat.
You can configure an optional heart beat / keep-alive, sent every 45 seconds
when there is no any traffic to make sure that load balancers do not kill an
idle connection.
```
webSocket.setHeartBeatPeriod(45);
```

11
SECURITY.md Normal file
View File

@ -0,0 +1,11 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 7.x.x | :white_check_mark: |
## Reporting a Vulnerability
Users should send an email to bsergean@gmail.com to report a vulnerability.

View File

@ -1,14 +0,0 @@
image:
- Visual Studio 2017
install:
- ls -al
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
- cd test
- mkdir build
- cd build
- cmake -G"NMake Makefiles" ..
- nmake
- ixwebsocket_unittest.exe
build: off

View File

@ -1,33 +1,11 @@
version: "3"
version: "3.3"
services:
snake:
image: bsergean/ws:build
entrypoint: ws snake --port 8765 --host 0.0.0.0 --redis_hosts redis1
ports:
- "8765:8765"
networks:
- ws-net
push:
entrypoint: ws push_server --host 0.0.0.0
image: ${DOCKER_REPO}/ws:build
autoroute:
entrypoint: ws autoroute ws://push:8008
image: ${DOCKER_REPO}/ws:build
depends_on:
- redis1
ws:
security_opt:
- seccomp:unconfined
cap_add:
- SYS_PTRACE
stdin_open: true
tty: true
image: bsergean/ws:build
entrypoint: bash
networks:
- ws-net
depends_on:
- redis1
redis1:
image: redis:alpine
networks:
- ws-net
networks:
ws-net:
- push

39
docker/Dockerfile.alpine Normal file
View File

@ -0,0 +1,39 @@
FROM alpine:3.12 as build
RUN apk add --no-cache \
gcc g++ musl-dev linux-headers \
cmake mbedtls-dev make zlib-dev python3-dev ninja git
RUN addgroup -S app && \
adduser -S -G app app && \
chown -R app:app /opt && \
chown -R app:app /usr/local
# There is a bug in CMake where we cannot build from the root top folder
# So we build from /opt
COPY --chown=app:app . /opt
WORKDIR /opt
USER app
RUN make -f makefile.dev ws_mbedtls_install && \
sh tools/trim_repo_for_docker.sh
FROM alpine:3.12 as runtime
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 strace && \
addgroup -S app && \
adduser -S -G app app
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
# COPY --chown=app:app --from=build /opt /opt
RUN chmod +x /usr/local/bin/ws && \
ldd /usr/local/bin/ws
# Now run in usermode
USER app
WORKDIR /home/app
ENTRYPOINT ["ws"]
EXPOSE 8008

41
docker/Dockerfile.centos Normal file
View File

@ -0,0 +1,41 @@
FROM centos:8 as build
RUN yum install -y gcc-c++ make cmake zlib-devel openssl-devel redhat-rpm-config
RUN yum install -y epel-release
RUN yum install -y mbedtls-devel
RUN groupadd app && useradd -g app app
RUN chown -R app:app /opt
RUN chown -R app:app /usr/local
# There is a bug in CMake where we cannot build from the root top folder
# So we build from /opt
COPY --chown=app:app . /opt
WORKDIR /opt
USER app
RUN [ "make", "ws_mbedtls_install" ]
RUN [ "rm", "-rf", "build" ]
FROM centos:8 as runtime
RUN yum install -y gdb strace
RUN yum install -y epel-release
RUN yum install -y mbedtls
RUN groupadd app && useradd -g app app
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
RUN chmod +x /usr/local/bin/ws
RUN ldd /usr/local/bin/ws
# Copy source code for gcc
COPY --chown=app:app --from=build /opt /opt
# Now run in usermode
USER app
WORKDIR /home/app
ENTRYPOINT ["ws"]
EXPOSE 8008

26
docker/Dockerfile.centos7 Normal file
View File

@ -0,0 +1,26 @@
FROM centos:7 as build
RUN yum install -y gcc-c++ make zlib-devel openssl-devel redhat-rpm-config
RUN groupadd app && useradd -g app app
RUN chown -R app:app /opt
RUN chown -R app:app /usr/local
WORKDIR /tmp
RUN curl -O https://cmake.org/files/v3.14/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxvf cmake-3.14.0-Linux-x86_64.tar.gz
RUN cp -rf cmake-3.14.0-Linux-x86_64/* /usr/
RUN yum install -y git
# There is a bug in CMake where we cannot build from the root top folder
# So we build from /opt
COPY --chown=app:app . /opt
WORKDIR /opt
USER app
RUN [ "make", "ws_no_python" ]
RUN [ "rm", "-rf", "build" ]
ENTRYPOINT ["ws"]
CMD ["--help"]

View File

@ -2,14 +2,14 @@
FROM debian:buster as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++
RUN apt-get -y install g++
RUN apt-get -y install libssl-dev
RUN apt-get -y install libz-dev
RUN apt-get -y install make
@ -25,9 +25,9 @@ RUN ["make"]
FROM debian:buster as runtime
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
# Runtime
RUN apt-get install -y libssl1.1
RUN apt-get update
# Runtime
RUN apt-get install -y libssl1.1
RUN apt-get install -y ca-certificates
RUN ["update-ca-certificates"]

View File

@ -8,7 +8,7 @@ RUN yum install -y openssl-devel
RUN yum install -y wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
@ -16,6 +16,7 @@ ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
RUN yum install -y python
RUN yum install -y libtsan
RUN yum install -y zlib-devel
COPY . .
# RUN ["make", "test"]
@ -26,7 +27,7 @@ FROM fedora:30 as runtime
RUN yum install -y libtsan
RUN groupadd app && useradd -g app app
RUN groupadd app && useradd -g app app
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
RUN chmod +x /usr/local/bin/ws
RUN ldd /usr/local/bin/ws

View File

@ -0,0 +1,23 @@
# Build time
FROM ubuntu:bionic as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++
RUN apt-get -y install libssl-dev
RUN apt-get -y install libz-dev
RUN apt-get -y install make
RUN apt-get -y install python
COPY . .
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
RUN ["make", "ws"]

View File

@ -0,0 +1,24 @@
# Build time
FROM ubuntu:disco as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++
RUN apt-get -y install libssl-dev
RUN apt-get -y install libz-dev
RUN apt-get -y install make
RUN apt-get -y install python
COPY . .
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
# RUN ["make", "test"]
CMD ["sh"]

View File

@ -0,0 +1,23 @@
# Build time
FROM ubuntu:groovy as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install g++ libssl-dev libz-dev make python ninja-build
RUN apt-get -y install cmake
RUN apt-get -y install gdb
COPY . /opt
WORKDIR /opt
#
# To use the container interactively for debugging/building
# 1. Build with
# CMD ["ls"]
# 2. Run with
# docker run --entrypoint sh -it docker-game-eng-dev.addsrv.com/ws:9.10.6
#
RUN ["make", "test"]
# CMD ["ls"]

View File

@ -0,0 +1,27 @@
# Build time
FROM ubuntu:precise as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++
RUN apt-get -y install libssl-dev
RUN apt-get -y install libz-dev
RUN apt-get -y install make
RUN apt-get -y install python
RUN apt-get -y install git
COPY . .
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
RUN ["make", "ws_no_python"]
ENTRYPOINT ["ws"]
CMD ["--help"]

View File

@ -0,0 +1,22 @@
# Build time
FROM ubuntu:trusty as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++ libssl-dev libz-dev make python git
COPY . .
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
RUN ["make", "ws_no_python"]
ENTRYPOINT ["ws"]
CMD ["--help"]

View File

@ -2,14 +2,14 @@
FROM ubuntu:xenial as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++
RUN apt-get -y install g++
RUN apt-get -y install libssl-dev
RUN apt-get -y install libz-dev
RUN apt-get -y install make

1237
docs/CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

93
docs/build.md Normal file
View File

@ -0,0 +1,93 @@
## Build
### CMake
CMakefiles for the library and the examples are available. This library has few dependencies, so it is possible to just add the source files into your project. Otherwise the usual way will suffice.
```
mkdir build # make a build dir so that you can build out of tree.
cd build
cmake -DUSE_TLS=1 ..
make -j
make install # will install to /usr/local on Unix, on macOS it is a good idea to sudo chown -R `whoami`:staff /usr/local
```
Headers and a static library will be installed to the target dir.
There is a unittest which can be executed by typing `make test`.
Options for building:
* `-DBUILD_SHARED_LIBS=ON` will build the unittest as a shared libary instead of a static library, which is the default
* `-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_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows). When using a custom version of openssl (say a prebuilt version, odd runtime problems can happens, as in #319, and special cmake trickery will be required (see this [comment](https://github.com/machinezone/IXWebSocket/issues/175#issuecomment-620231032))
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
* `-DUSE_WS=1` will build the ws interactive command line tool
* `-DUSE_TEST=1` will build the unittest
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/unittest_windows.yml) which have instructions for building dependencies.
It is also possible to externally include the project, so that everything is fetched over the wire when you build like so:
```
ExternalProject_Add(
IXWebSocket
GIT_REPOSITORY https://github.com/machinezone/IXWebSocket.git
...
)
```
### vcpkg
It is possible to get IXWebSocket through Microsoft [vcpkg](https://github.com/microsoft/vcpkg).
```
vcpkg install ixwebsocket
```
To use the installed package within a cmake project, use the following:
```cmake
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") # this is super important in order for cmake to include the vcpkg search/lib paths!
# find library and its headers
find_path(IXWEBSOCKET_INCLUDE_DIR ixwebsocket/IXWebSocket.h)
find_library(IXWEBSOCKET_LIBRARY ixwebsocket)
# include headers
include_directories(${IXWEBSOCKET_INCLUDE_DIR})
# ...
target_link_libraries(${PROJECT_NAME} ... ${IXWEBSOCKET_LIBRARY}) # Cmake will automatically fail the generation if the lib was not found, i.e is set to NOTFOUND
```
### Conan
[ ![Download](https://api.bintray.com/packages/conan/conan-center/ixwebsocket%3A_/images/download.svg) ](https://bintray.com/conan/conan-center/ixwebsocket%3A_/_latestVersion)
Conan is currently supported through a recipe in [Conan Center](https://github.com/conan-io/conan-center-index/tree/master/recipes/ixwebsocket) ([Bintray entry](https://bintray.com/conan/conan-center/ixwebsocket%3A_)).
Package reference
* Conan 1.21.0 and up: `ixwebsocket/7.9.2`
* Earlier versions: `ixwebsocket/7.9.2@_/_`
Note that the version listed here might not be the latest one. See Bintray or the recipe itself for the latest version. If you're migrating from the previous, custom Bintray remote, note that the package reference _has_ to be lower-case.
### Docker
There is a Dockerfile for running the unittest on Linux, and to run the `ws` tool. It is also available on the docker registry.
```
docker run docker.pkg.github.com/machinezone/ixwebsocket/ws:latest --help
```
To use docker-compose you must make a docker container first.
```
$ make docker
...
$ docker compose up &
...
$ docker exec -it ixwebsocket_ws_1 bash
app@ca2340eb9106:~$ ws --help
ws is a websocket tool
...
```

76
docs/design.md Normal file
View File

@ -0,0 +1,76 @@
## Implementation details
### Per Message Deflate compression.
The per message deflate compression option is supported. It can lead to very nice bandbwith savings (20x !) if your messages are similar, which is often the case for example for chat applications. All features of the spec should be supported.
### TLS/SSL
Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and macOS, OpenSSL and mbedTLS can be used on Android, Linux and Windows.
If you are using OpenSSL, try to be on a version higher than 1.1.x as there there are thread safety problems with 1.0.x.
### Polling and background thread work
No manual polling to fetch data is required. Data is sent and received instantly by using a background thread for receiving data and the select [system](http://man7.org/linux/man-pages/man2/select.2.html) call to be notified by the OS of incoming data. No timeout is used for select so that the background thread is only woken up when data is available, to optimize battery life. This is also the recommended way of using select according to the select tutorial, section [select law](https://linux.die.net/man/2/select_tut). Read and Writes to the socket are non blocking. Data is sent right away and not enqueued by writing directly to the socket, which is [possible](https://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid) since system socket implementations allow concurrent read/writes.
### Automatic reconnection
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds. This behavior can be disabled.
### Large messages
Large frames are broken up into smaller chunks or messages to avoid filling up the os tcp buffers, which is permitted thanks to WebSocket [fragmentation](https://tools.ietf.org/html/rfc6455#section-5.4). Messages up to 1G were sent and received succesfully.
### Testing
The library has an interactive tool which is handy for testing compatibility ith other libraries. We have tested our client against Python, Erlang, Node.js, and C++ websocket server libraries.
The unittest tries to be comprehensive, and has been running on multiple platforms, with different sanitizers such as a thread sanitizer to catch data races or the undefined behavior sanitizer.
The regression test is running after each commit on github actions for multiple configurations.
## Limitations
* On some configuration (mostly Android) certificate validation needs to be setup so that SocketTLSOptions.caFile point to a pem file, such as the one distributed by Firefox. Unless that setup is done connecting to a wss endpoint will display an error. With mbedtls the message will contain `error in handshake : X509 - Certificate verification failed, e.g. CRL, CA or signature check failed`.
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.
## C++ code organization
Here is a simplistic diagram which explains how the code is structured in term of class/modules.
```
+-----------------------+ --- Public
| | Start the receiving Background thread. Auto reconnection. Simple websocket Ping.
| IXWebSocket | Interface used by C++ test clients. No IX dependencies.
| |
+-----------------------+
| |
| IXWebSocketServer | Run a server and give each connections its own WebSocket object.
| | Each connection is handled in a new OS thread.
| |
+-----------------------+ --- Private
| |
| IXWebSocketTransport | Low level websocket code, framing, managing raw socket. Adapted from easywsclient.
| |
+-----------------------+
| |
| IXWebSocketHandshake | Establish the connection between client and server.
| |
+-----------------------+
| |
| IXWebSocket | ws:// Unencrypted Socket handler
| IXWebSocketAppleSSL | wss:// TLS encrypted Socket AppleSSL handler. Used on iOS and macOS
| IXWebSocketOpenSSL | wss:// TLS encrypted Socket OpenSSL handler. Used on Android and Linux
| | Can be used on macOS too.
+-----------------------+
| |
| IXSocketConnect | Connect to the remote host (client).
| |
+-----------------------+
| |
| IXDNSLookup | Does DNS resolution asynchronously so that it can be interrupted.
| |
+-----------------------+
```

149
docs/index.md Normal file
View File

@ -0,0 +1,149 @@
## Hello world
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Note that the MinGW compiler is not supported at this point. Two important design goals are simplicity and correctness.
A bad security bug affecting users compiling with SSL enabled and OpenSSL as the backend was just fixed in newly released version 11.0.0. Please upgrade ! (more details in the [https://github.com/machinezone/IXWebSocket/pull/250](PR).
```cpp
/*
* main.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*
* Super simple standalone example. See ws folder, unittest and doc/usage.md for more.
*
* On macOS
* $ mkdir -p build ; (cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install)
* $ clang++ --std=c++11 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
* $ ./a.out
*
* Or use cmake -DBUILD_DEMO=ON option for other platforms
*/
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXUserAgent.h>
#include <iostream>
int main()
{
// Required on Windows
ix::initNetSystem();
// Our websocket object
ix::WebSocket webSocket;
// Connect to a server with encryption
// See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration
std::string url("wss://echo.websocket.org");
webSocket.setUrl(url);
std::cout << "Connecting to " << url << "..." << std::endl;
// 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([](const ix::WebSocketMessagePtr& msg)
{
if (msg->type == ix::WebSocketMessageType::Message)
{
std::cout << "received message: " << msg->str << std::endl;
std::cout << "> " << std::flush;
}
else if (msg->type == ix::WebSocketMessageType::Open)
{
std::cout << "Connection established" << std::endl;
std::cout << "> " << std::flush;
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
// Maybe SSL is not configured properly
std::cout << "Connection error: " << msg->errorInfo.reason << std::endl;
std::cout << "> " << std::flush;
}
}
);
// Now that our callback is setup, we can start our background thread and receive messages
webSocket.start();
// Send a message to the server (default to TEXT mode)
webSocket.send("hello world");
// Display a prompt
std::cout << "> " << std::flush;
std::string text;
// Read text from the console and send messages in text mode.
// Exit with Ctrl-D on Unix or Ctrl-Z on Windows.
while (std::getline(std::cin, text))
{
webSocket.send(text);
std::cout << "> " << std::flush;
}
return 0;
}
```
Interested? Go read the [docs](https://machinezone.github.io/IXWebSocket/)! If things don't work as expected, please create an issue on GitHub, or even better a pull request if you know how to fix your problem.
IXWebSocket is actively being developed, check out the [changelog](https://machinezone.github.io/IXWebSocket/CHANGELOG/) to know what's cooking. If you are looking for a real time messaging service (the chat-like 'server' your websocket code will talk to) with many features such as history, backed by Redis, look at [cobra](https://github.com/machinezone/cobra).
IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code.
Starting with the 11.0.8 release, IXWebSocket should be fully C++11 compatible.
## Users
If your company or project is using this library, feel free to open an issue or PR to amend this list.
- [Machine Zone](https://www.mz.com)
- [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings.
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework.
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod
- [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper
- [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots
- [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar
## Alternative libraries
There are plenty of great websocket libraries out there, which might work for you. Here are a couple of serious ones.
* [websocketpp](https://github.com/zaphoyd/websocketpp) - C++
* [beast](https://github.com/boostorg/beast) - C++
* [libwebsockets](https://libwebsockets.org/) - C
* [µWebSockets](https://github.com/uNetworking/uWebSockets) - C
* [wslay](https://github.com/tatsuhiro-t/wslay) - C
[uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv.
To check the performance of a websocket library, you can look at the [autoroute](https://github.com/bsergean/autoroute) project.
## Continuous Integration
| OS | TLS | Sanitizer | Status |
|-------------------|-------------------|-------------------|-------------------|
| Linux | OpenSSL | None | [![Build2][1]][0] |
| macOS | Secure Transport | Thread Sanitizer | [![Build2][2]][0] |
| macOS | OpenSSL | Thread Sanitizer | [![Build2][3]][0] |
| macOS | MbedTLS | Thread Sanitizer | [![Build2][4]][0] |
| Windows | Disabled | None | [![Build2][5]][0] |
| UWP | Disabled | None | [![Build2][6]][0] |
| Linux | OpenSSL | Address Sanitizer | [![Build2][7]][0] |
| Mingw | Disabled | None | [![Build2][8]][0] |
* ASAN fails on Linux because of a known problem, we need a
* Some tests are disabled on Windows/UWP because of a pathing problem
* TLS and ZLIB are disabled on Windows/UWP because enabling make the CI run takes a lot of time, for setting up vcpkg.
[0]: https://github.com/machinezone/IXWebSocket
[1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg
[2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg
[3]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_openssl/badge.svg
[4]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_mbedtls/badge.svg
[5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg
[6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg
[7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg
[8]: https://github.com/machinezone/IXWebSocket/workflows/unittest_windows_gcc/badge.svg

94
docs/packages.md Normal file
View File

@ -0,0 +1,94 @@
Notes on how we can update the different packages for ixwebsocket.
## VCPKG
Visit the [releases](https://github.com/machinezone/IXWebSocket/releases) page on Github. A tag must have been made first.
Download the latest entry.
```
$ cd /tmp
/tmp$ curl -s -O -L https://github.com/machinezone/IXWebSocket/archive/v9.1.9.tar.gz
/tmp$
/tmp$ openssl sha512 v9.1.9.tar.gz
SHA512(v9.1.9.tar.gz)= f1fd731b5f6a9ce6d6d10bee22a5d9d9baaa8ea0564d6c4cd7eb91dcb88a45c49b2c7fdb75f8640a3589c1b30cee33ef5df8dcbb55920d013394d1e33ddd3c8e
```
Now go punch those values in the vcpkg ixwebsocket port config files. Here is what the diff look like.
```
vcpkg$ git diff
diff --git a/ports/ixwebsocket/CONTROL b/ports/ixwebsocket/CONTROL
index db9c2adc9..4acae5c3f 100644
--- a/ports/ixwebsocket/CONTROL
+++ b/ports/ixwebsocket/CONTROL
@@ -1,5 +1,5 @@
Source: ixwebsocket
-Version: 8.0.5
+Version: 9.1.9
Build-Depends: zlib
Homepage: https://github.com/machinezone/IXWebSocket
Description: Lightweight WebSocket Client and Server + HTTP Client and Server
diff --git a/ports/ixwebsocket/portfile.cmake b/ports/ixwebsocket/portfile.cmake
index de082aece..68e523a05 100644
--- a/ports/ixwebsocket/portfile.cmake
+++ b/ports/ixwebsocket/portfile.cmake
@@ -1,8 +1,8 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO machinezone/IXWebSocket
- REF v8.0.5
- SHA512 9dcc20d9a0629b92c62a68a8bd7c8206f18dbd9e93289b0b687ec13c478ce9ad1f3563b38c399c8277b0d3812cc78ca725786ba1dedbc3445b9bdb9b689e8add
+ REF v9.1.9
+ SHA512 f1fd731b5f6a9ce6d6d10bee22a5d9d9baaa8ea0564d6c4cd7eb91dcb88a45c49b2c7fdb75f8640a3589c1b30cee33ef5df8dcbb55920d013394d1e33ddd3c8e
)
```
You will need a fork of the vcpkg repo to make a pull request.
```
git fetch upstream
git co master
git reset --hard upstream/master
git push origin master --force
```
Make the pull request (I use a new branch to do that).
```
vcpkg$ git co -b feature/ixwebsocket_9.1.9
M ports/ixwebsocket/CONTROL
M ports/ixwebsocket/portfile.cmake
Switched to a new branch 'feature/ixwebsocket_9.1.9'
vcpkg$
vcpkg$
vcpkg$ git commit -am 'ixwebsocket: update to 9.1.9'
[feature/ixwebsocket_9.1.9 8587a4881] ixwebsocket: update to 9.1.9
2 files changed, 3 insertions(+), 3 deletions(-)
vcpkg$
vcpkg$ git push
fatal: The current branch feature/ixwebsocket_9.1.9 has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin feature/ixwebsocket_9.1.9
vcpkg$ git push --set-upstream origin feature/ixwebsocket_9.1.9
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 8 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 4), reused 0 (delta 0)
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
remote:
remote: Create a pull request for 'feature/ixwebsocket_9.1.9' on GitHub by visiting:
remote: https://github.com/bsergean/vcpkg/pull/new/feature/ixwebsocket_9.1.9
remote:
To https://github.com/bsergean/vcpkg.git
* [new branch] feature/ixwebsocket_9.1.9 -> feature/ixwebsocket_9.1.9
Branch 'feature/ixwebsocket_9.1.9' set up to track remote branch 'feature/ixwebsocket_9.1.9' from 'origin' by rebasing.
vcpkg$
```
Just visit this url, https://github.com/bsergean/vcpkg/pull/new/feature/ixwebsocket_9.1.9, printed on the console, to make the pull request.

37
docs/performance.md Normal file
View 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
```

643
docs/usage.md Normal file
View File

@ -0,0 +1,643 @@
# Examples
The [*ws*](https://github.com/machinezone/IXWebSocket/tree/master/ws) folder countains many interactive programs for chat, [file transfers](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_send.cpp), [curl like](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_http_client.cpp) http clients, demonstrating client and server usage.
## Windows note
To use the network system on Windows, you need to initialize it once with *WSAStartup()* and clean it up with *WSACleanup()*. We have helpers for that which you can use, see below. This init would typically take place in your main function.
```cpp
#include <ixwebsocket/IXNetSystem.h>
int main()
{
ix::initNetSystem();
...
ix::uninitNetSystem();
return 0;
}
```
## WebSocket client API
```cpp
#include <ixwebsocket/IXWebSocket.h>
...
// Our websocket object
ix::WebSocket webSocket;
std::string url("ws://localhost:8080/");
webSocket.setUrl(url);
// Optional heart beat, sent every 45 seconds when there is not any traffic
// to make sure that load balancers do not kill an idle connection.
webSocket.setPingInterval(45);
// Per message deflate connection is enabled by default. You can tweak its parameters or disable it
webSocket.disablePerMessageDeflate();
// Setup a callback to be fired when a message or an event (open, close, error) is received
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
{
if (msg->type == ix::WebSocketMessageType::Message)
{
std::cout << msg->str << std::endl;
}
}
);
// Now that our callback is setup, we can start our background thread and receive messages
webSocket.start();
// Send a message to the server (default to TEXT mode)
webSocket.send("hello world");
// The message can be sent in BINARY mode (useful if you send MsgPack data for example)
webSocket.sendBinary("some serialized binary data");
// ... finally ...
// Stop the connection
webSocket.stop()
```
### Sending messages
`WebSocketSendInfo result = websocket.send("foo")` will send a message.
If the connection was closed, sending will fail, and the success field of the result object will be set to false. There could also be a compression error in which case the compressError field will be set to true. The payloadSize field and wireSize fields will tell you respectively how much bytes the message weight, and how many bytes were sent on the wire (potentially compressed + counting the message header (a few bytes).
There is an optional progress callback that can be passed in as the second argument. If a message is large it will be fragmented into chunks which will be sent independantly. Everytime the we can write a fragment into the OS network cache, the callback will be invoked. If a user wants to cancel a slow send, false should be returned from within the callback.
Here is an example code snippet copied from the ws send sub-command. Each fragment weights 32K, so the total integer is the wireSize divided by 32K. As an example if you are sending 32M of data, uncompressed, total will be 1000. current will be set to 0 for the first fragment, then 1, 2 etc...
```
auto result =
_webSocket.sendBinary(serializedMsg, [this, throttle](int current, int total) -> bool {
spdlog::info("ws_send: Step {} out of {}", current + 1, total);
if (throttle)
{
std::chrono::duration<double, std::milli> duration(10);
std::this_thread::sleep_for(duration);
}
return _connected;
});
```
The `send()` and `sendText()` methods check that the string contains only valid UTF-8 characters. If you know that the string is a valid UTF-8 string you can skip that step and use the `sendUtf8Text` method instead.
With the IXWebSocketSendData overloads of `sendUtf8Text` and `sendBinary` it is possible to not only send std::string but also `std::vector<char>`, `std::vector<uint8_t>` and `char*`.
```
std::vector<uint8_t> data({1, 2, 3, 4});
auto result = webSocket.sendBinary(data);
const char* text = "Hello World!";
result = webSocket.sendUtf8Text(IXWebSocketSendData(text, strlen(text)));
```
### ReadyState
`getReadyState()` returns the state of the connection. There are 4 possible states.
1. ReadyState::Connecting - The connection is not yet open.
2. ReadyState::Open - The connection is open and ready to communicate.
3. ReadyState::Closing - The connection is in the process of closing.
4. ReadyState::Closed - The connection is closed or could not be opened.
### Open and Close notifications
The onMessage event will be fired when the connection is opened or closed. This is similar to the [JavaScript browser API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket), which has `open` and `close` events notification that can be registered with the browser `addEventListener`.
```cpp
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
{
if (msg->type == ix::WebSocketMessageType::Open)
{
std::cout << "send greetings" << std::endl;
// Headers can be inspected (pairs of string/string)
std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
std::cout << "disconnected" << std::endl;
// The server can send an explicit code and reason for closing.
// This data can be accessed through the closeInfo object.
std::cout << msg->closeInfo.code << std::endl;
std::cout << msg->closeInfo.reason << std::endl;
}
}
);
```
### Error notification
A message will be fired when there is an error with the connection. The message type will be `ix::WebSocketMessageType::Error`. Multiple fields will be available on the event to describe the error.
```cpp
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
{
if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
std::cout << ss.str() << std::endl;
}
}
);
```
### start, stop
1. `websocket.start()` connect to the remote server and starts the message receiving background thread.
2. `websocket.stop()` disconnect from the remote server and closes the background thread.
### Configuring the remote url
The url can be set and queried after a websocket object has been created. You will have to call `stop` and `start` if you want to disconnect and connect to that new url.
```cpp
std::string url("wss://example.com");
websocket.configure(url);
```
### Ping/Pong support
Ping/pong messages are used to implement keep-alive. 2 message types exists to identify ping and pong messages. Note that when a ping message is received, a pong is instantly send back as requested by the WebSocket spec.
```cpp
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
{
if (msg->type == ix::WebSocketMessageType::Ping ||
msg->type == ix::WebSocketMessageType::Pong)
{
std::cout << "pong data: " << msg->str << std::endl;
}
}
);
```
A ping message can be sent to the server, with an optional data string.
```cpp
websocket.ping("ping data, optional (empty string is ok): limited to 125 bytes long");
```
### Heartbeat.
You can configure an optional heart beat / keep-alive, sent every 45 seconds
when there is no any traffic to make sure that load balancers do not kill an
idle connection.
```cpp
webSocket.setPingInterval(45);
```
### Supply extra HTTP headers.
You can set extra HTTP headers to be sent during the WebSocket handshake.
```cpp
WebSocketHttpHeaders headers;
headers["foo"] = "bar";
webSocket.setExtraHeaders(headers);
```
### Subprotocols
You can specify subprotocols to be set during the WebSocket handshake. For more info you can refer to [this doc](https://hpbn.co/websocket/#subprotocol-negotiation).
```cpp
webSocket.addSubprotocol("appProtocol-v1");
webSocket.addSubprotocol("appProtocol-v2");
```
The protocol that the server did accept is available in the open info `protocol` field.
```cpp
std::cout << "protocol: " << msg->openInfo.protocol << std::endl;
```
### Automatic reconnection
Automatic reconnection kicks in when the connection is disconnected without the user consent. This feature is on by default and can be turned off.
```cpp
webSocket.enableAutomaticReconnection(); // turn on
webSocket.disableAutomaticReconnection(); // turn off
bool enabled = webSocket.isAutomaticReconnectionEnabled(); // query state
```
The technique to calculate wait time is called [exponential
backoff](https://docs.aws.amazon.com/general/latest/gr/api-retries.html). Here
are the default waiting times between attempts (from connecting with `ws connect ws://foo.com`)
```
> Connection error: Got bad status connecting to foo.com, status: 301, HTTP Status line: HTTP/1.1 301 Moved Permanently
#retries: 1
Wait time(ms): 100
#retries: 2
Wait time(ms): 200
#retries: 3
Wait time(ms): 400
#retries: 4
Wait time(ms): 800
#retries: 5
Wait time(ms): 1600
#retries: 6
Wait time(ms): 3200
#retries: 7
Wait time(ms): 6400
#retries: 8
Wait time(ms): 10000
```
The waiting time is capped by default at 10s between 2 attempts, but that value
can be changed and queried. The minimum waiting time can also be set.
```cpp
webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s
uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
webSocket.setMinWaitBetweenReconnectionRetries(1000); // 1000ms = 1s
uint32_t m = webSocket.getMinWaitBetweenReconnectionRetries();
```
## Handshake timeout
You can control how long to wait until timing out while waiting for the websocket handshake to be performed.
```
int handshakeTimeoutSecs = 1;
setHandshakeTimeout(handshakeTimeoutSecs);
```
## WebSocket server API
### Legacy api
This api was actually changed to take a weak_ptr<WebSocket> as the first argument to setOnConnectionCallback ; previously it would take a shared_ptr<WebSocket> which was creating cycles and then memory leaks problems.
```cpp
#include <ixwebsocket/IXWebSocketServer.h>
...
// Run a server on localhost at a given port.
// Bound host name, max connections and listen backlog can also be passed in as parameters.
int port = 8008;
std::string host("127.0.0.1"); // If you need this server to be accessible on a different machine, use "0.0.0.0"
ix::WebSocketServer server(port, host);
server.setOnConnectionCallback(
[&server](std::weak_ptr<WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState)
{
std::cout << "Remote ip: " << connectionState->remoteIp << std::endl;
auto ws = webSocket.lock();
if (ws)
{
ws->setOnMessageCallback(
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
{
if (msg->type == ix::WebSocketMessageType::Open)
{
std::cout << "New connection" << std::endl;
// A connection state object is available, and has a default id
// You can subclass ConnectionState and pass an alternate factory
// to override it. It is useful if you want to store custom
// attributes per connection (authenticated bool flag, attributes, etc...)
std::cout << "id: " << connectionState->getId() << std::endl;
// The uri the client did connect to.
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
// For an echo server, we just send back to the client whatever was received by the server
// All connected clients are available in an std::set. See the broadcast cpp example.
// Second parameter tells whether we are sending the message in binary or text mode.
// Here we send it in the same mode as it was received.
auto ws = webSocket.lock();
if (ws)
{
ws->send(msg->str, msg->binary);
}
}
}
}
);
}
);
auto res = server.listen();
if (!res.first)
{
// Error handling
return 1;
}
// Per message deflate connection is enabled by default. It can be disabled
// which might be helpful when running on low power devices such as a Rasbery Pi
server.disablePerMessageDeflate();
// Run the server in the background. Server can be stoped by calling server.stop()
server.start();
// Block until server.stop() is called.
server.wait();
```
### New api
The new API does not require to use 2 nested callbacks, which is a bit annoying. The real fix is that there was a memory leak due to a shared_ptr cycle, due to passing down a shared_ptr<WebSocket> down to the callbacks.
The webSocket reference is guaranteed to be always valid ; by design the callback will never be invoked with a null webSocket object.
```cpp
#include <ixwebsocket/IXWebSocketServer.h>
...
// Run a server on localhost at a given port.
// Bound host name, max connections and listen backlog can also be passed in as parameters.
int port = 8008;
std::string host("127.0.0.1"); // If you need this server to be accessible on a different machine, use "0.0.0.0"
ix::WebSocketServer server(port, host);
server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) {
// The ConnectionState object contains information about the connection,
// at this point only the client ip address and the port.
std::cout << "Remote ip: " << connectionState->getRemoteIp() << std::endl;
if (msg->type == ix::WebSocketMessageType::Open)
{
std::cout << "New connection" << std::endl;
// A connection state object is available, and has a default id
// You can subclass ConnectionState and pass an alternate factory
// to override it. It is useful if you want to store custom
// attributes per connection (authenticated bool flag, attributes, etc...)
std::cout << "id: " << connectionState->getId() << std::endl;
// The uri the client did connect to.
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
std::cout << "\t" << it.first << ": " << it.second << std::endl;
}
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
// For an echo server, we just send back to the client whatever was received by the server
// All connected clients are available in an std::set. See the broadcast cpp example.
// Second parameter tells whether we are sending the message in binary or text mode.
// Here we send it in the same mode as it was received.
std::cout << "Received: " << msg->str << std::endl;
webSocket.send(msg->str, msg->binary);
}
});
auto res = server.listen();
if (!res.first)
{
// Error handling
return 1;
}
// Per message deflate connection is enabled by default. It can be disabled
// which might be helpful when running on low power devices such as a Rasbery Pi
server.disablePerMessageDeflate();
// Run the server in the background. Server can be stoped by calling server.stop()
server.start();
// Block until server.stop() is called.
server.wait();
```
### Heartbeat
You can configure an optional heartbeat / keep-alive for the WebSocket server. The heartbeat interval can be adjusted or disabled when constructing the `WebSocketServer`. Setting the interval to `-1` disables the heartbeat feature; this is the default setting. The parameter you set will be applied to every `WebSocket` object that the server creates.
To enable a 45 second heartbeat on a `WebSocketServer`:
```cpp
int pingIntervalSeconds = 45;
ix::WebSocketServer server(port, host, backlog, maxConnections, handshakeTimeoutSecs, addressFamily, pingIntervalSeconds);
```
## HTTP client API
```cpp
#include <ixwebsocket/IXHttpClient.h>
...
//
// Preparation
//
HttpClient httpClient;
HttpRequestArgsPtr args = httpClient.createRequest();
// Custom headers can be set
WebSocketHttpHeaders headers;
headers["Foo"] = "bar";
args->extraHeaders = headers;
// Timeout options
args->connectTimeout = connectTimeout;
args->transferTimeout = transferTimeout;
// Redirect options
args->followRedirects = followRedirects;
args->maxRedirects = maxRedirects;
// Misc
args->compress = compress; // Enable gzip compression
args->verbose = verbose;
args->logger = [](const std::string& msg)
{
std::cout << msg;
};
//
// Synchronous Request
//
HttpResponsePtr out;
std::string url = "https://www.google.com";
// HEAD request
out = httpClient.head(url, args);
// GET request
out = httpClient.get(url, args);
// POST request with parameters
HttpParameters httpParameters;
httpParameters["foo"] = "bar";
// HTTP form data can be passed in as well, for multi-part upload of files
HttpFormDataParameters httpFormDataParameters;
httpParameters["baz"] = "booz";
out = httpClient.post(url, httpParameters, httpFormDataParameters, args);
// POST request with a body
out = httpClient.post(url, std::string("foo=bar"), args);
// PUT and PATCH are available too.
//
// Result
//
auto statusCode = response->statusCode; // Can be HttpErrorCode::Ok, HttpErrorCode::UrlMalformed, etc...
auto errorCode = response->errorCode; // 200, 404, etc...
auto responseHeaders = response->headers; // All the headers in a special case-insensitive unordered_map of (string, string)
auto body = response->body; // All the bytes from the response as an std::string
auto errorMsg = response->errorMsg; // Descriptive error message in case of failure
auto uploadSize = response->uploadSize; // Byte count of uploaded data
auto downloadSize = response->downloadSize; // Byte count of downloaded data
//
// Asynchronous Request
//
bool async = true;
HttpClient httpClient(async);
auto args = httpClient.createRequest(url, HttpClient::kGet);
// If you define a chunk callback it will be called repeteadly with the
// incoming data. This allows to process data on the go or write it to disk
// instead of accumulating the data in memory.
args.onChunkCallback = [](const std::string& data)
{
// process data
};
// Push the request to a queue,
bool ok = httpClient.performRequest(args, [](const HttpResponsePtr& response)
{
// This callback execute in a background thread. Make sure you uses appropriate protection such as mutex
auto statusCode = response->statusCode; // acess results
// response->body is empty if onChunkCallback was used
}
);
// ok will be false if your httpClient is not async
// A request in progress can be cancelled by setting the cancel flag. It does nothing if the request already completed.
args->cancel = true;
```
See this [issue](https://github.com/machinezone/IXWebSocket/issues/209) for links about uploading files with HTTP multipart.
## HTTP server API
```cpp
#include <ixwebsocket/IXHttpServer.h>
ix::HttpServer server(port, hostname);
auto res = server.listen();
if (!res.first)
{
std::cerr << res.second << std::endl;
return 1;
}
server.start();
server.wait();
```
If you want to handle how requests are processed, implement the setOnConnectionCallback callback, which takes an HttpRequestPtr as input, and returns an HttpResponsePtr. You can look at HttpServer::setDefaultConnectionCallback for a slightly more advanced callback example.
```cpp
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
{
// Build a string for the response
std::stringstream ss;
ss << connectionState->getRemoteIp();
<< " "
<< request->method
<< " "
<< request->uri;
std::string content = ss.str();
return std::make_shared<HttpResponse>(200, "OK",
HttpErrorCode::Ok,
WebSocketHttpHeaders(),
content);
}
```
## TLS support and configuration
To leverage TLS features, the library must be compiled with the option `USE_TLS=1`.
If you are using OpenSSL, try to be on a version higher than 1.1.x as there there are thread safety problems with 1.0.x.
Then, secure sockets are automatically used when connecting to a `wss://*` url.
Additional TLS options can be configured by passing a `ix::SocketTLSOptions` instance to the
`setTLSOptions` on `ix::WebSocket` (or `ix::WebSocketServer` or `ix::HttpServer`)
```cpp
webSocket.setTLSOptions({
.certFile = "path/to/cert/file.pem",
.keyFile = "path/to/key/file.pem",
.caFile = "path/to/trust/bundle/file.pem", // as a file, or in memory buffer in PEM format
.tls = true // required in server mode
});
```
Specifying `certFile` and `keyFile` configures the certificate that will be used to communicate with TLS peers.
On a client, this is only necessary for connecting to servers that require a client certificate.
On a server, this is necessary for TLS support.
Specifying `caFile` configures the trusted roots bundle file (in PEM format) that will be used to verify peer certificates.
- The special value of `SYSTEM` (the default) indicates that the system-configured trust bundle should be used; this is generally what you want when connecting to any publicly exposed API/server.
- The special value of `NONE` can be used to disable peer verification; this is only recommended to rule out certificate verification when testing connectivity.
- If the value contain the special value `-----BEGIN CERTIFICATE-----`, the value will be read from memory, and not from a file. This is convenient on platforms like Android where reading / writing to the file system can be challenging without proper permissions, or without knowing the location of a temp directory.
For a client, specifying `caFile` can be used if connecting to a server that uses a self-signed cert, or when using a custom CA in an internal environment.
For a server, specifying `caFile` implies that:
1. You require clients to present a certificate
1. It must be signed by one of the trusted roots in the file
By default, a destination's hostname is always validated against the certificate that it presents. To accept certificates with any hostname, set `ix::SocketTLSOptions::disable_hostname_validation` to `true`.

308
docs/ws.md Normal file
View File

@ -0,0 +1,308 @@
## General
ws is a command line tool that should exercise most of the IXWebSocket code, and provide example code.
```
ws is a websocket tool
Usage: ws [OPTIONS] SUBCOMMAND
Options:
-h,--help Print this help message and exit
Subcommands:
send Send a file
receive Receive a file
transfer Broadcasting server
connect Connect to a remote server
chat Group chat
echo_server Echo server
broadcast_server Broadcasting server
ping Ping pong
curl HTTP Client
httpd HTTP server
```
## curl
The curl subcommand try to be compatible with the curl syntax, to fetch http pages.
Making a HEAD request with the -I parameter.
```
$ ws curl -I https://www.google.com/
Accept-Ranges: none
Alt-Svc: quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
Date: Tue, 08 Oct 2019 21:36:57 GMT
Expires: -1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
Set-Cookie: NID=188=ASwfz8GrXQrHCLqAz-AndLOMLcz0rC9yecnf3h0yXZxRL3rTufTU_GDDwERp7qQL7LZ_EB8gCRyPXGERyOSAgaqgnrkoTmvWrwFemRLMaOZ896GrHobi5fV7VLklnSG2w48Gj8xMlwxfP7Z-bX-xR9UZxep1tHM6UmFQdD_GkBE; expires=Wed, 08-Apr-2020 21:36:57 GMT; path=/; domain=.google.com; HttpOnly
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
Upload size: 143
Download size: 0
Status: 200
```
Making a POST request with the -F parameter.
```
$ ws curl -F foo=bar https://httpbin.org/post
foo: bar
Downloaded 438 bytes out of 438
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Encoding:
Content-Length: 438
Content-Type: application/json
Date: Tue, 08 Oct 2019 21:47:54 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Upload size: 219
Download size: 438
Status: 200
payload: {
"args": {},
"data": "",
"files": {},
"form": {
"foo": "bar"
},
"headers": {
"Accept": "*/*",
"Content-Length": "7",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "ixwebsocket/7.0.0 macos ssl/OpenSSL OpenSSL 1.0.2q 20 Nov 2018 zlib 1.2.11"
},
"json": null,
"origin": "155.94.127.118, 155.94.127.118",
"url": "https://httpbin.org/post"
}
```
Passing in a custom header with -H.
```
$ ws curl -F foo=bar -H 'my_custom_header: baz' https://httpbin.org/post
my_custom_header: baz
foo: bar
Downloaded 470 bytes out of 470
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Encoding:
Content-Length: 470
Content-Type: application/json
Date: Tue, 08 Oct 2019 21:50:25 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Upload size: 243
Download size: 470
Status: 200
payload: {
"args": {},
"data": "",
"files": {},
"form": {
"foo": "bar"
},
"headers": {
"Accept": "*/*",
"Content-Length": "7",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"My-Custom-Header": "baz",
"User-Agent": "ixwebsocket/7.0.0 macos ssl/OpenSSL OpenSSL 1.0.2q 20 Nov 2018 zlib 1.2.11"
},
"json": null,
"origin": "155.94.127.118, 155.94.127.118",
"url": "https://httpbin.org/post"
}
```
## connect
The connect command connects to a websocket endpoint, and starts an interactive prompt. Line editing, such as using the direction keys to fetch the last thing you tried to type) is provided. That command is pretty useful to try to send random data to an endpoint and verify that the service handles it with grace (such as sending invalid json).
```
ws connect wss://echo.websocket.org
Type Ctrl-D to exit prompt...
Connecting to url: wss://echo.websocket.org
> ws_connect: connected
Uri: /
Handshake Headers:
Connection: Upgrade
Date: Tue, 08 Oct 2019 21:38:44 GMT
Sec-WebSocket-Accept: 2j6LBScZveqrMx1W/GJkCWvZo3M=
sec-websocket-extensions:
Server: Kaazing Gateway
Upgrade: websocket
Received ping
Received ping
Received ping
Hello world !
> Received 13 bytes
ws_connect: received message: Hello world !
> Hello world !
> Received 13 bytes
ws_connect: received message: Hello world !
```
```
ws connect 'ws://jeanserge.com/v2?appkey=_pubsub'
Type Ctrl-D to exit prompt...
Connecting to url: ws://jeanserge.com/v2?appkey=_pubsub
> ws_connect: connected
Uri: /v2?appkey=_pubsub
Handshake Headers:
Connection: Upgrade
Date: Tue, 08 Oct 2019 21:45:28 GMT
Sec-WebSocket-Accept: LYHmjh9Gsu/Yw7aumQqyPObOEV4=
Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
Server: Python/3.7 websockets/8.0.2
Upgrade: websocket
bababababababab
> ws_connect: connection closed: code 1000 reason
ws_connect: connected
Uri: /v2?appkey=_pubsub
Handshake Headers:
Connection: Upgrade
Date: Tue, 08 Oct 2019 21:45:44 GMT
Sec-WebSocket-Accept: I1rqxdLgTU+opPi5/zKPBTuXdLw=
Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
Server: Python/3.7 websockets/8.0.2
Upgrade: websocket
```
It is possible to pass custom HTTP header when doing the connection handshake,
the remote server might process them to implement a simple authorization
scheme.
```
src$ ws connect -H Authorization:supersecret ws://localhost:8008
Type Ctrl-D to exit prompt...
[2020-12-17 22:35:08.732] [info] Authorization: supersecret
Connecting to url: ws://localhost:8008
> [2020-12-17 22:35:08.736] [info] ws_connect: connected
[2020-12-17 22:35:08.736] [info] Uri: /
[2020-12-17 22:35:08.736] [info] Headers:
[2020-12-17 22:35:08.736] [info] Connection: Upgrade
[2020-12-17 22:35:08.736] [info] Sec-WebSocket-Accept: 2yaTFcdwn8KL6IzSMj2u6Le7KTg=
[2020-12-17 22:35:08.736] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
[2020-12-17 22:35:08.736] [info] Server: ixwebsocket/11.0.4 macos ssl/SecureTransport zlib 1.2.11
[2020-12-17 22:35:08.736] [info] Upgrade: websocket
[2020-12-17 22:35:08.736] [info] Received 25 bytes
ws_connect: received message: Authorization suceeded!
[2020-12-17 22:35:08.736] [info] Received pong ixwebsocket::heartbeat::30s::0
hello
> [2020-12-17 22:35:25.157] [info] Received 7 bytes
ws_connect: received message: hello
```
If the wrong header is passed in, the server would close the connection with a custom close code (>4000, and <4999).
```
[2020-12-17 22:39:37.044] [info] Upgrade: websocket
ws_connect: connection closed: code 4001 reason Permission denied
```
## echo server
The ws echo server will respond what the client just sent him. If we use the
simple --http_authorization_header we can enforce that client need to pass a
special value in the Authorization header to connect.
```
$ ws echo_server --http_authorization_header supersecret
[2020-12-17 22:35:06.192] [info] Listening on 127.0.0.1:8008
[2020-12-17 22:35:08.735] [info] New connection
[2020-12-17 22:35:08.735] [info] remote ip: 127.0.0.1
[2020-12-17 22:35:08.735] [info] id: 0
[2020-12-17 22:35:08.735] [info] Uri: /
[2020-12-17 22:35:08.735] [info] Headers:
[2020-12-17 22:35:08.735] [info] Authorization: supersecret
[2020-12-17 22:35:08.735] [info] Connection: Upgrade
[2020-12-17 22:35:08.735] [info] Host: localhost:8008
[2020-12-17 22:35:08.735] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
[2020-12-17 22:35:08.735] [info] Sec-WebSocket-Key: eFF2Gf25dC7eC15Ab1135G==
[2020-12-17 22:35:08.735] [info] Sec-WebSocket-Version: 13
[2020-12-17 22:35:08.735] [info] Upgrade: websocket
[2020-12-17 22:35:08.735] [info] User-Agent: ixwebsocket/11.0.4 macos ssl/SecureTransport zlib 1.2.11
[2020-12-17 22:35:25.157] [info] Received 7 bytes
```
## Websocket proxy
```
ws proxy_server --remote_host ws://127.0.0.1:9000 -v
Listening on 127.0.0.1:8008
```
If you connect to ws://127.0.0.1:8008, the proxy will connect to ws://127.0.0.1:9000 and pass all traffic to this server.
You can also use a more complex setup if you want to redirect to different websocket servers based on the hostname your client is trying to connect to. If you have multiple CNAME aliases that point to the same server.
A JSON config file is used to express that mapping ; here connecting to echo.jeanserge.com will proxy the client to ws://localhost:8008 on the local machine (which actually runs ws echo_server), while connecting to bavarde.jeanserge.com will proxy the client to ws://localhost:5678 where a cobra python server is running. As a side note you will need a wildcard SSL certificate if you want to have SSL enabled on that machine.
```
echo.jeanserge.com=ws://localhost:8008
bavarde.jeanserge.com=ws://localhost:5678
```
The --config_path option is required to instruct ws proxy_server to read that file.
```
ws proxy_server --config_path proxyConfig.json --port 8765
```
## File transfer
```
# Start transfer server, which is just a broadcast server at this point
ws transfer # running on port 8080.
# Start receiver first
ws receive ws://localhost:8080
# Then send a file. File will be received and written to disk by the receiver process
ws send ws://localhost:8080 /file/to/path
```
## HTTP Client
```
$ ws curl --help
HTTP Client
Usage: ws curl [OPTIONS] url
Positionals:
url TEXT REQUIRED Connection url
Options:
-h,--help Print this help message and exit
-d TEXT Form data
-F TEXT Form data
-H TEXT Header
--output TEXT Output file
-I Send a HEAD request
-L Follow redirects
--max-redirects INT Max Redirects
-v Verbose
-O Save output to disk
--compress Enable gzip compression
--connect-timeout INT Connection timeout
--transfer-timeout INT Transfer timeout
```

46
httpd.cpp Normal file
View File

@ -0,0 +1,46 @@
/*
* httpd.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*
* Buid with make httpd
*/
#include <ixwebsocket/IXHttpServer.h>
#include <sstream>
#include <iostream>
int main(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0]
<< " <port> <host>" << std::endl;
std::cerr << " " << argv[0] << " 9090 127.0.0.1" << std::endl;
std::cerr << " " << argv[0] << " 9090 0.0.0.0" << std::endl;
return 1;
}
int port;
std::stringstream ss;
ss << argv[1];
ss >> port;
std::string hostname(argv[2]);
std::cout << "Listening on " << hostname
<< ":" << port << std::endl;
ix::HttpServer server(port, hostname);
auto res = server.listen();
if (!res.first)
{
std::cout << res.second << std::endl;
return 1;
}
server.start();
server.wait();
return 0;
}

View File

@ -0,0 +1,9 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
if (@USE_ZLIB@)
find_dependency(ZLIB)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-targets.cmake")

11
ixwebsocket.pc.in Normal file
View File

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: ixwebsocket
Description: websocket and http client and server library, with TLS support and very few dependencies
Version: @CMAKE_PROJECT_VERSION@
Libs: -L${libdir} -lixwebsocket
Cflags: -I${includedir}
Requires: @requires@

125
ixwebsocket/IXBase64.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef _MACARON_BASE64_H_
#define _MACARON_BASE64_H_
/**
* The MIT License (MIT)
* Copyright (c) 2016 tomykaira
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <cstdint>
#include <string>
namespace macaron {
class Base64 {
public:
static std::string Encode(const std::string data) {
static constexpr char sEncodingTable[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
size_t in_len = data.size();
size_t out_len = 4 * ((in_len + 2) / 3);
std::string ret(out_len, '\0');
size_t i;
char *p = const_cast<char*>(ret.c_str());
for (i = 0; i < in_len - 2; i += 3) {
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)];
*p++ = sEncodingTable[data[i + 2] & 0x3F];
}
if (i < in_len) {
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
if (i == (in_len - 1)) {
*p++ = sEncodingTable[((data[i] & 0x3) << 4)];
*p++ = '=';
}
else {
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)];
}
*p++ = '=';
}
return ret;
}
static std::string Decode(const std::string& input, std::string& out) {
static constexpr unsigned char kDecodingTable[] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};
size_t in_len = input.size();
if (in_len % 4 != 0) return "Input data size is not a multiple of 4";
size_t out_len = in_len / 4 * 3;
if (input[in_len - 1] == '=') out_len--;
if (input[in_len - 2] == '=') out_len--;
out.resize(out_len);
for (size_t i = 0, j = 0; i < in_len;) {
uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);
if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF;
if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF;
if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF;
}
return "";
}
};
}
#endif /* _MACARON_BASE64_H_ */

61
ixwebsocket/IXBench.cpp Normal file
View File

@ -0,0 +1,61 @@
/*
* IXBench.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXBench.h"
#include <iostream>
namespace ix
{
Bench::Bench(const std::string& description)
: _description(description)
{
reset();
}
Bench::~Bench()
{
if (!_reported)
{
report();
}
}
void Bench::reset()
{
_start = std::chrono::high_resolution_clock::now();
_reported = false;
}
void Bench::report()
{
auto now = std::chrono::high_resolution_clock::now();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
_duration = microseconds.count();
std::cerr << _description << " completed in " << _duration << " us" << std::endl;
setReported();
}
void Bench::record()
{
auto now = std::chrono::high_resolution_clock::now();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
_duration = microseconds.count();
}
void Bench::setReported()
{
_reported = true;
}
uint64_t Bench::getDuration() const
{
return _duration;
}
} // namespace ix

32
ixwebsocket/IXBench.h Normal file
View File

@ -0,0 +1,32 @@
/*
* IXBench.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <chrono>
#include <cstdint>
#include <string>
namespace ix
{
class Bench
{
public:
Bench(const std::string& description);
~Bench();
void reset();
void record();
void report();
void setReported();
uint64_t getDuration() const;
private:
std::string _description;
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
uint64_t _duration;
bool _reported;
};
} // namespace ix

View File

@ -6,18 +6,20 @@
#include "IXCancellationRequest.h"
#include <cassert>
#include <chrono>
namespace ix
{
CancellationRequest makeCancellationRequestWithTimeout(int secs,
std::atomic<bool>& requestInitCancellation)
CancellationRequest makeCancellationRequestWithTimeout(
int secs, std::atomic<bool>& requestInitCancellation)
{
assert(secs > 0);
auto start = std::chrono::system_clock::now();
auto timeout = std::chrono::seconds(secs);
auto isCancellationRequested = [&requestInitCancellation, start, timeout]() -> bool
{
auto isCancellationRequested = [&requestInitCancellation, start, timeout]() -> bool {
// Was an explicit cancellation requested ?
if (requestInitCancellation) return true;
@ -30,4 +32,4 @@ namespace ix
return isCancellationRequested;
}
}
} // namespace ix

View File

@ -6,14 +6,13 @@
#pragma once
#include <functional>
#include <atomic>
#include <functional>
namespace ix
{
using CancellationRequest = std::function<bool()>;
CancellationRequest makeCancellationRequestWithTimeout(int seconds,
std::atomic<bool>& requestInitCancellation);
}
CancellationRequest makeCancellationRequestWithTimeout(
int seconds, std::atomic<bool>& requestInitCancellation);
} // namespace ix

View File

@ -10,7 +10,8 @@ namespace ix
{
std::atomic<uint64_t> ConnectionState::_globalId(0);
ConnectionState::ConnectionState() : _terminated(false)
ConnectionState::ConnectionState()
: _terminated(false)
{
computeId();
}
@ -30,6 +31,11 @@ namespace ix
return std::make_shared<ConnectionState>();
}
void ConnectionState::setOnSetTerminatedCallback(const OnSetTerminatedCallback& callback)
{
_onSetTerminatedCallback = callback;
}
bool ConnectionState::isTerminated() const
{
return _terminated;
@ -38,6 +44,30 @@ namespace ix
void ConnectionState::setTerminated()
{
_terminated = true;
}
}
if (_onSetTerminatedCallback)
{
_onSetTerminatedCallback();
}
}
const std::string& ConnectionState::getRemoteIp()
{
return _remoteIp;
}
int ConnectionState::getRemotePort()
{
return _remotePort;
}
void ConnectionState::setRemoteIp(const std::string& remoteIp)
{
_remoteIp = remoteIp;
}
void ConnectionState::setRemotePort(int remotePort)
{
_remotePort = remotePort;
}
} // namespace ix

View File

@ -6,14 +6,18 @@
#pragma once
#include <stdint.h>
#include <string>
#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
namespace ix
{
class ConnectionState {
using OnSetTerminatedCallback = std::function<void()>;
class ConnectionState
{
public:
ConnectionState();
virtual ~ConnectionState() = default;
@ -24,14 +28,27 @@ namespace ix
void setTerminated();
bool isTerminated() const;
const std::string& getRemoteIp();
int getRemotePort();
static std::shared_ptr<ConnectionState> createConnectionState();
private:
void setOnSetTerminatedCallback(const OnSetTerminatedCallback& callback);
void setRemoteIp(const std::string& remoteIp);
void setRemotePort(int remotePort);
protected:
std::atomic<bool> _terminated;
std::string _id;
OnSetTerminatedCallback _onSetTerminatedCallback;
static std::atomic<uint64_t> _globalId;
std::string _remoteIp;
int _remotePort;
friend class SocketServer;
};
}
} // namespace ix

View File

@ -4,38 +4,58 @@
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include "IXDNSLookup.h"
#include "IXNetSystem.h"
//
// On Windows Universal Platform (uwp), gai_strerror defaults behavior is to returns wchar_t
// which is different from all other platforms. We want the non unicode version.
// See https://github.com/microsoft/vcpkg/pull/11030
// We could do this in IXNetSystem.cpp but so far we are only using gai_strerror in here.
//
#ifdef _UNICODE
#undef _UNICODE
#endif
#ifdef UNICODE
#undef UNICODE
#endif
#include <string.h>
#include "IXDNSLookup.h"
#include "IXNetSystem.h"
#include <chrono>
#include <string.h>
#include <thread>
#include <utility>
// mingw build quirks
#if defined(_WIN32) && defined(__GNUC__)
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV NI_NUMERICSERV
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG LUP_ADDRCONFIG
#endif
#endif
#ifdef __APPLE__
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0
#endif
#endif
namespace ix
{
const int64_t DNSLookup::kDefaultWait = 10; // ms
const int64_t DNSLookup::kDefaultWait = 1; // ms
std::atomic<uint64_t> DNSLookup::_nextId(0);
std::set<uint64_t> DNSLookup::_activeJobs;
std::mutex DNSLookup::_activeJobsMutex;
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait) :
_port(port),
_wait(wait),
_res(nullptr),
_done(false),
_id(_nextId++)
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait)
: _hostname(hostname)
, _port(port)
, _wait(wait)
, _res(nullptr)
, _done(false)
{
setHostname(hostname);
;
}
DNSLookup::~DNSLookup()
{
// Remove this job from the active jobs list
std::lock_guard<std::mutex> lock(_activeJobsMutex);
_activeJobs.erase(_id);
}
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
DNSLookup::AddrInfoPtr DNSLookup::getAddrInfo(const std::string& hostname,
int port,
std::string& errMsg)
{
@ -48,41 +68,40 @@ namespace ix
std::string sport = std::to_string(port);
struct addrinfo* res;
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(),
&hints, &res);
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(), &hints, &res);
if (getaddrinfo_result)
{
errMsg = gai_strerror(getaddrinfo_result);
res = nullptr;
}
return res;
return AddrInfoPtr{ res, freeaddrinfo };
}
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
DNSLookup::AddrInfoPtr DNSLookup::resolve(std::string& errMsg,
const CancellationRequest& isCancellationRequested,
bool blocking)
bool cancellable)
{
return blocking ? resolveBlocking(errMsg, isCancellationRequested)
: resolveAsync(errMsg, isCancellationRequested);
return cancellable ? resolveCancellable(errMsg, isCancellationRequested)
: resolveUnCancellable(errMsg, isCancellationRequested);
}
struct addrinfo* DNSLookup::resolveBlocking(std::string& errMsg,
const CancellationRequest& isCancellationRequested)
DNSLookup::AddrInfoPtr DNSLookup::resolveUnCancellable(
std::string& errMsg, const CancellationRequest& isCancellationRequested)
{
errMsg = "no error";
// Maybe a cancellation request got in before the background thread terminated ?
if (isCancellationRequested && isCancellationRequested())
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
}
return getAddrInfo(getHostname(), _port, errMsg);
return getAddrInfo(_hostname, _port, errMsg);
}
struct addrinfo* DNSLookup::resolveAsync(std::string& errMsg,
const CancellationRequest& isCancellationRequested)
DNSLookup::AddrInfoPtr DNSLookup::resolveCancellable(
std::string& errMsg, const CancellationRequest& isCancellationRequested)
{
errMsg = "no error";
@ -94,33 +113,31 @@ namespace ix
// if you need a second lookup.
}
// Record job in the active Job set
{
std::lock_guard<std::mutex> lock(_activeJobsMutex);
_activeJobs.insert(_id);
}
//
// Good resource on thread forced termination
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
//
_thread = std::thread(&DNSLookup::run, this, _id, getHostname(), _port);
_thread.detach();
auto ptr = shared_from_this();
std::weak_ptr<DNSLookup> self(ptr);
std::unique_lock<std::mutex> lock(_conditionVariableMutex);
int port = _port;
std::string hostname(_hostname);
// We make the background thread doing the work a shared pointer
// instead of a member variable, because it can keep running when
// this object goes out of scope, in case of cancellation
auto t = std::make_shared<std::thread>(&DNSLookup::run, this, self, hostname, port);
t->detach();
while (!_done)
{
// Wait for 10 milliseconds on the condition variable, to see
// if the bg thread has terminated.
if (_condition.wait_for(lock, std::chrono::milliseconds(_wait)) == std::cv_status::no_timeout)
{
// Background thread has terminated, so we can break of this loop
break;
}
// Wait for 1 milliseconds, to see if the bg thread has terminated.
// We do not use a condition variable to wait, as destroying this one
// if the bg thread is alive can cause undefined behavior.
std::this_thread::sleep_for(std::chrono::milliseconds(_wait));
// Were we cancelled ?
if (isCancellationRequested && isCancellationRequested())
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
@ -128,7 +145,7 @@ namespace ix
}
// Maybe a cancellation request got in before the bg terminated ?
if (isCancellationRequested && isCancellationRequested())
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
@ -138,41 +155,24 @@ namespace ix
return getRes();
}
void DNSLookup::run(uint64_t id, const std::string& hostname, int port) // thread runner
void DNSLookup::run(std::weak_ptr<DNSLookup> self,
std::string hostname,
int port) // thread runner
{
// We don't want to read or write into members variables of an object that could be
// gone, so we use temporary variables (res) or we pass in by copy everything that
// getAddrInfo needs to work.
std::string errMsg;
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
auto res = getAddrInfo(hostname, port, errMsg);
// if this isn't an active job, and the control thread is gone
// there is nothing to do, and we don't want to touch the defunct
// object data structure such as _errMsg or _condition
std::lock_guard<std::mutex> lock(_activeJobsMutex);
if (_activeJobs.count(id) == 0)
if (auto lock = self.lock())
{
return;
// Copy result into the member variables
setRes(res);
setErrMsg(errMsg);
_done = true;
}
// Copy result into the member variables
setRes(res);
setErrMsg(errMsg);
_condition.notify_one();
_done = true;
}
void DNSLookup::setHostname(const std::string& hostname)
{
std::lock_guard<std::mutex> lock(_hostnameMutex);
_hostname = hostname;
}
const std::string& DNSLookup::getHostname()
{
std::lock_guard<std::mutex> lock(_hostnameMutex);
return _hostname;
}
void DNSLookup::setErrMsg(const std::string& errMsg)
@ -187,15 +187,15 @@ namespace ix
return _errMsg;
}
void DNSLookup::setRes(struct addrinfo* addr)
void DNSLookup::setRes(DNSLookup::AddrInfoPtr addr)
{
std::lock_guard<std::mutex> lock(_resMutex);
_res = addr;
_res = std::move(addr);
}
struct addrinfo* DNSLookup::getRes()
DNSLookup::AddrInfoPtr DNSLookup::getRes()
{
std::lock_guard<std::mutex> lock(_resMutex);
return _res;
}
}
} // namespace ix

View File

@ -11,71 +11,57 @@
#pragma once
#include "IXCancellationRequest.h"
#include <string>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <memory>
#include <mutex>
#include <set>
#include <string>
struct addrinfo;
namespace ix
{
class DNSLookup {
class DNSLookup : public std::enable_shared_from_this<DNSLookup>
{
public:
DNSLookup(const std::string& hostname,
int port,
int64_t wait = DNSLookup::kDefaultWait);
~DNSLookup();
using AddrInfoPtr = std::shared_ptr<addrinfo>;
DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait);
~DNSLookup() = default;
struct addrinfo* resolve(std::string& errMsg,
AddrInfoPtr resolve(std::string& errMsg,
const CancellationRequest& isCancellationRequested,
bool blocking = false);
bool cancellable = true);
private:
struct addrinfo* resolveAsync(std::string& errMsg,
const CancellationRequest& isCancellationRequested);
struct addrinfo* resolveBlocking(std::string& errMsg,
const CancellationRequest& isCancellationRequested);
AddrInfoPtr resolveCancellable(std::string& errMsg,
const CancellationRequest& isCancellationRequested);
AddrInfoPtr resolveUnCancellable(std::string& errMsg,
const CancellationRequest& isCancellationRequested);
static struct addrinfo* getAddrInfo(const std::string& hostname,
AddrInfoPtr getAddrInfo(const std::string& hostname,
int port,
std::string& errMsg);
void run(uint64_t id, const std::string& hostname, int port); // thread runner
void setHostname(const std::string& hostname);
const std::string& getHostname();
void run(std::weak_ptr<DNSLookup> self, std::string hostname, int port); // thread runner
void setErrMsg(const std::string& errMsg);
const std::string& getErrMsg();
void setRes(struct addrinfo* addr);
struct addrinfo* getRes();
void setRes(AddrInfoPtr addr);
AddrInfoPtr getRes();
std::string _hostname;
std::mutex _hostnameMutex;
int _port;
int64_t _wait;
const static int64_t kDefaultWait;
struct addrinfo* _res;
AddrInfoPtr _res;
std::mutex _resMutex;
std::string _errMsg;
std::mutex _errMsgMutex;
std::atomic<bool> _done;
std::thread _thread;
std::condition_variable _condition;
std::mutex _conditionVariableMutex;
uint64_t _id;
static std::atomic<uint64_t> _nextId;
static std::set<uint64_t> _activeJobs;
static std::mutex _activeJobsMutex;
const static int64_t kDefaultWait;
};
}
} // namespace ix

View File

@ -0,0 +1,44 @@
/*
* IXExponentialBackoff.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#include "IXExponentialBackoff.h"
#include <cmath>
namespace ix
{
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount,
uint32_t maxWaitBetweenReconnectionRetries,
uint32_t minWaitBetweenReconnectionRetries)
{
// It's easy with a power function to go beyond 2^32, and then
// have unexpected results, so prepare for that
const uint32_t maxRetryCountWithoutOverflow = 26;
uint32_t waitTime = 0;
if (retryCount < maxRetryCountWithoutOverflow)
{
waitTime = std::pow(2, retryCount) * 100;
}
if (waitTime < minWaitBetweenReconnectionRetries)
{
waitTime = minWaitBetweenReconnectionRetries;
}
if (waitTime > maxWaitBetweenReconnectionRetries)
{
waitTime = maxWaitBetweenReconnectionRetries;
}
if (retryCount >= maxRetryCountWithoutOverflow)
{
waitTime = maxWaitBetweenReconnectionRetries;
}
return waitTime;
}
} // namespace ix

View File

@ -0,0 +1,16 @@
/*
* IXExponentialBackoff.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <cstdint>
namespace ix
{
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount,
uint32_t maxWaitBetweenReconnectionRetries,
uint32_t minWaitBetweenReconnectionRetries);
} // namespace ix

View File

@ -0,0 +1,97 @@
/*
* IXGetFreePort.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone. All rights reserved.
*/
// Using inet_addr will trigger an error on uwp without this
// FIXME: use a different api
#ifdef _WIN32
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#endif
#include "IXGetFreePort.h"
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXSocket.h>
#include <random>
#include <string>
namespace ix
{
int getAnyFreePortRandom()
{
std::random_device rd;
std::uniform_int_distribution<int> dist(1024 + 1, 65535);
return dist(rd);
}
int getAnyFreePort()
{
socket_t sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
return getAnyFreePortRandom();
}
int enable = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*) &enable, sizeof(enable)) < 0)
{
return getAnyFreePortRandom();
}
// Bind to port 0. This is the standard way to get a free port.
struct sockaddr_in server; // server address information
server.sin_family = AF_INET;
server.sin_port = htons(0);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(sockfd, (struct sockaddr*) &server, sizeof(server)) < 0)
{
Socket::closeSocket(sockfd);
return getAnyFreePortRandom();
}
struct sockaddr_in sa; // server address information
socklen_t len = sizeof(sa);
if (getsockname(sockfd, (struct sockaddr*) &sa, &len) < 0)
{
Socket::closeSocket(sockfd);
return getAnyFreePortRandom();
}
int port = ntohs(sa.sin_port);
Socket::closeSocket(sockfd);
return port;
}
int getFreePort()
{
while (true)
{
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
int port = getAnyFreePortRandom();
#else
int port = getAnyFreePort();
#endif
#else
int port = getAnyFreePort();
#endif
//
// Only port above 1024 can be used by non root users, but for some
// reason I got port 7 returned with macOS when binding on port 0...
//
if (port > 1024)
{
return port;
}
}
return -1;
}
} // namespace ix

View File

@ -0,0 +1,12 @@
/*
* IXGetFreePort.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone. All rights reserved.
*/
#pragma once
namespace ix
{
int getFreePort();
} // namespace ix

120
ixwebsocket/IXGzipCodec.cpp Normal file
View File

@ -0,0 +1,120 @@
/*
* IXGzipCodec.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXGzipCodec.h"
#include "IXBench.h"
#include <array>
#include <string.h>
#ifdef IXWEBSOCKET_USE_ZLIB
#include <zlib.h>
#endif
namespace ix
{
std::string gzipCompress(const std::string& str)
{
#ifndef IXWEBSOCKET_USE_ZLIB
return std::string();
#else
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
// deflateInit2 configure the file format: request gzip instead of deflate
const int windowBits = 15;
const int GZIP_ENCODING = 16;
deflateInit2(&zs,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
windowBits | GZIP_ENCODING,
8,
Z_DEFAULT_STRATEGY);
zs.next_in = (Bytef*) str.data();
zs.avail_in = (uInt) str.size(); // set the z_stream's input
int ret;
char outbuffer[32768];
std::string outstring;
// retrieve the compressed bytes blockwise
do
{
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out)
{
// append the block to the output string
outstring.append(outbuffer, zs.total_out - outstring.size());
}
} while (ret == Z_OK);
deflateEnd(&zs);
return outstring;
#endif // IXWEBSOCKET_USE_ZLIB
}
#ifdef IXWEBSOCKET_USE_DEFLATE
static uint32_t loadDecompressedGzipSize(const uint8_t* p)
{
return ((uint32_t) p[0] << 0) | ((uint32_t) p[1] << 8) | ((uint32_t) p[2] << 16) |
((uint32_t) p[3] << 24);
}
#endif
bool gzipDecompress(const std::string& in, std::string& out)
{
#ifndef IXWEBSOCKET_USE_ZLIB
return false;
#else
z_stream inflateState;
memset(&inflateState, 0, sizeof(inflateState));
inflateState.zalloc = Z_NULL;
inflateState.zfree = Z_NULL;
inflateState.opaque = Z_NULL;
inflateState.avail_in = 0;
inflateState.next_in = Z_NULL;
if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK)
{
return false;
}
inflateState.avail_in = (uInt) in.size();
inflateState.next_in = (unsigned char*) (const_cast<char*>(in.data()));
const int kBufferSize = 1 << 14;
std::array<unsigned char, kBufferSize> compressBuffer;
do
{
inflateState.avail_out = (uInt) kBufferSize;
inflateState.next_out = &compressBuffer.front();
int ret = inflate(&inflateState, Z_SYNC_FLUSH);
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
inflateEnd(&inflateState);
return false;
}
out.append(reinterpret_cast<char*>(&compressBuffer.front()),
kBufferSize - inflateState.avail_out);
} while (inflateState.avail_out == 0);
inflateEnd(&inflateState);
return true;
#endif // IXWEBSOCKET_USE_ZLIB
}
} // namespace ix

15
ixwebsocket/IXGzipCodec.h Normal file
View File

@ -0,0 +1,15 @@
/*
* IXGzipCodec.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
std::string gzipCompress(const std::string& str);
bool gzipDecompress(const std::string& in, std::string& out);
} // namespace ix

217
ixwebsocket/IXHttp.cpp Normal file
View File

@ -0,0 +1,217 @@
/*
* IXHttp.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#include "IXHttp.h"
#include "IXCancellationRequest.h"
#include "IXGzipCodec.h"
#include "IXSocket.h"
#include <sstream>
#include <vector>
namespace ix
{
std::string Http::trim(const std::string& str)
{
std::string out;
for (auto c : str)
{
if (c != ' ' && c != '\n' && c != '\r')
{
out += c;
}
}
return out;
}
std::pair<std::string, int> Http::parseStatusLine(const std::string& line)
{
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
std::string token;
std::stringstream tokenStream(line);
std::vector<std::string> tokens;
// Split by ' '
while (std::getline(tokenStream, token, ' '))
{
tokens.push_back(token);
}
std::string httpVersion;
if (tokens.size() >= 1)
{
httpVersion = trim(tokens[0]);
}
int statusCode = -1;
if (tokens.size() >= 2)
{
std::stringstream ss;
ss << trim(tokens[1]);
ss >> statusCode;
}
return std::make_pair(httpVersion, statusCode);
}
std::tuple<std::string, std::string, std::string> Http::parseRequestLine(
const std::string& line)
{
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
std::string token;
std::stringstream tokenStream(line);
std::vector<std::string> tokens;
// Split by ' '
while (std::getline(tokenStream, token, ' '))
{
tokens.push_back(token);
}
std::string method;
if (tokens.size() >= 1)
{
method = trim(tokens[0]);
}
std::string requestUri;
if (tokens.size() >= 2)
{
requestUri = trim(tokens[1]);
}
std::string httpVersion;
if (tokens.size() >= 3)
{
httpVersion = trim(tokens[2]);
}
return std::make_tuple(method, requestUri, httpVersion);
}
std::tuple<bool, std::string, HttpRequestPtr> Http::parseRequest(
std::unique_ptr<Socket>& socket, int timeoutSecs)
{
HttpRequestPtr httpRequest;
std::atomic<bool> requestInitCancellation(false);
auto isCancellationRequested =
makeCancellationRequestWithTimeout(timeoutSecs, requestInitCancellation);
// Read first line
auto lineResult = socket->readLine(isCancellationRequested);
auto lineValid = lineResult.first;
auto line = lineResult.second;
if (!lineValid)
{
return std::make_tuple(false, "Error reading HTTP request line", httpRequest);
}
// Parse request line (GET /foo HTTP/1.1\r\n)
auto requestLine = Http::parseRequestLine(line);
auto method = std::get<0>(requestLine);
auto uri = std::get<1>(requestLine);
auto httpVersion = std::get<2>(requestLine);
// Retrieve and validate HTTP headers
auto result = parseHttpHeaders(socket, isCancellationRequested);
auto headersValid = result.first;
auto headers = result.second;
if (!headersValid)
{
return std::make_tuple(false, "Error parsing HTTP headers", httpRequest);
}
std::string body;
if (headers.find("Content-Length") != headers.end())
{
int contentLength = 0;
{
const char* p = headers["Content-Length"].c_str();
char* p_end{};
errno = 0;
long val = std::strtol(p, &p_end, 10);
if (p_end == p // invalid argument
|| errno == ERANGE // out of range
|| val < std::numeric_limits<int>::min()
|| val > std::numeric_limits<int>::max()) {
return std::make_tuple(
false, "Error parsing HTTP Header 'Content-Length'", httpRequest);
}
contentLength = val;
}
if (contentLength < 0)
{
return std::make_tuple(
false, "Error: 'Content-Length' should be a positive integer", httpRequest);
}
auto res = socket->readBytes(contentLength, nullptr, nullptr, isCancellationRequested);
if (!res.first)
{
return std::make_tuple(
false, std::string("Error reading request: ") + res.second, httpRequest);
}
body = res.second;
}
// If the content was compressed with gzip, decode it
if (headers["Content-Encoding"] == "gzip")
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string decompressedPayload;
if (!gzipDecompress(body, decompressedPayload))
{
return std::make_tuple(
false, std::string("Error during gzip decompression of the body"), httpRequest);
}
body = decompressedPayload;
#else
std::string errorMsg("ixwebsocket was not compiled with gzip support on");
return std::make_tuple(false, errorMsg, httpRequest);
#endif
}
httpRequest = std::make_shared<HttpRequest>(uri, method, httpVersion, body, headers);
return std::make_tuple(true, "", httpRequest);
}
bool Http::sendResponse(HttpResponsePtr response, std::unique_ptr<Socket>& socket)
{
// Write the response to the socket
std::stringstream ss;
ss << "HTTP/1.1 ";
ss << response->statusCode;
ss << " ";
ss << response->description;
ss << "\r\n";
if (!socket->writeBytes(ss.str(), nullptr))
{
return false;
}
// Write headers
ss.str("");
ss << "Content-Length: " << response->body.size() << "\r\n";
for (auto&& it : response->headers)
{
ss << it.first << ": " << it.second << "\r\n";
}
ss << "\r\n";
if (!socket->writeBytes(ss.str(), nullptr))
{
return false;
}
return response->body.empty() ? true : socket->writeBytes(response->body, nullptr);
}
} // namespace ix

135
ixwebsocket/IXHttp.h Normal file
View File

@ -0,0 +1,135 @@
/*
* IXHttp.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXProgressCallback.h"
#include "IXWebSocketHttpHeaders.h"
#include <atomic>
#include <cstdint>
#include <tuple>
#include <unordered_map>
namespace ix
{
enum class HttpErrorCode : int
{
Ok = 0,
CannotConnect = 1,
Timeout = 2,
Gzip = 3,
UrlMalformed = 4,
CannotCreateSocket = 5,
SendError = 6,
ReadError = 7,
CannotReadStatusLine = 8,
MissingStatus = 9,
HeaderParsingError = 10,
MissingLocation = 11,
TooManyRedirects = 12,
ChunkReadError = 13,
CannotReadBody = 14,
Cancelled = 15,
Invalid = 100
};
struct HttpResponse
{
int statusCode;
std::string description;
HttpErrorCode errorCode;
WebSocketHttpHeaders headers;
std::string body;
std::string errorMsg;
uint64_t uploadSize;
uint64_t downloadSize;
HttpResponse(int s = 0,
const std::string& des = std::string(),
const HttpErrorCode& c = HttpErrorCode::Ok,
const WebSocketHttpHeaders& h = WebSocketHttpHeaders(),
const std::string& b = std::string(),
const std::string& e = std::string(),
uint64_t u = 0,
uint64_t d = 0)
: statusCode(s)
, description(des)
, errorCode(c)
, headers(h)
, body(b)
, errorMsg(e)
, uploadSize(u)
, downloadSize(d)
{
;
}
};
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
using HttpParameters = std::unordered_map<std::string, std::string>;
using HttpFormDataParameters = std::unordered_map<std::string, std::string>;
using Logger = std::function<void(const std::string&)>;
using OnResponseCallback = std::function<void(const HttpResponsePtr&)>;
struct HttpRequestArgs
{
std::string url;
std::string verb;
WebSocketHttpHeaders extraHeaders;
std::string body;
std::string multipartBoundary;
int connectTimeout = 60;
int transferTimeout = 1800;
bool followRedirects = true;
int maxRedirects = 5;
bool verbose = false;
bool compress = true;
bool compressRequest = false;
Logger logger;
OnProgressCallback onProgressCallback;
OnChunkCallback onChunkCallback;
std::atomic<bool> cancel;
};
using HttpRequestArgsPtr = std::shared_ptr<HttpRequestArgs>;
struct HttpRequest
{
std::string uri;
std::string method;
std::string version;
std::string body;
WebSocketHttpHeaders headers;
HttpRequest(const std::string& u,
const std::string& m,
const std::string& v,
const std::string& b,
const WebSocketHttpHeaders& h = WebSocketHttpHeaders())
: uri(u)
, method(m)
, version(v)
, body(b)
, headers(h)
{
}
};
using HttpRequestPtr = std::shared_ptr<HttpRequest>;
class Http
{
public:
static std::tuple<bool, std::string, HttpRequestPtr> parseRequest(
std::unique_ptr<Socket>& socket, int timeoutSecs);
static bool sendResponse(HttpResponsePtr response, std::unique_ptr<Socket>& socket);
static std::pair<std::string, int> parseStatusLine(const std::string& line);
static std::tuple<std::string, std::string, std::string> parseRequestLine(
const std::string& line);
static std::string trim(const std::string& str);
};
} // namespace ix

View File

@ -5,95 +5,242 @@
*/
#include "IXHttpClient.h"
#include "IXUrlParser.h"
#include "IXWebSocketHttpHeaders.h"
#include "IXGzipCodec.h"
#include "IXSocketFactory.h"
#include <sstream>
#include <iomanip>
#include <vector>
#include "IXUrlParser.h"
#include "IXUserAgent.h"
#include "IXWebSocketHttpHeaders.h"
#include <assert.h>
#include <cstdint>
#include <cstring>
#include <zlib.h>
#include <iomanip>
#include <random>
#include <sstream>
#include <vector>
namespace ix
{
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
const std::string HttpClient::kPost = "POST";
const std::string HttpClient::kGet = "GET";
const std::string HttpClient::kHead = "HEAD";
const std::string HttpClient::kDelete = "DELETE";
const std::string HttpClient::kPut = "PUT";
const std::string HttpClient::kPatch = "PATCH";
HttpClient::HttpClient()
HttpClient::HttpClient(bool async)
: _async(async)
, _stop(false)
, _forceBody(false)
{
if (!_async) return;
_thread = std::thread(&HttpClient::run, this);
}
HttpClient::~HttpClient()
{
if (!_thread.joinable()) return;
_stop = true;
_condition.notify_one();
_thread.join();
}
HttpResponse HttpClient::request(
const std::string& url,
const std::string& verb,
const std::string& body,
const HttpRequestArgs& args,
int redirects)
void HttpClient::setTLSOptions(const SocketTLSOptions& tlsOptions)
{
_tlsOptions = tlsOptions;
}
void HttpClient::setForceBody(bool value)
{
_forceBody = value;
}
HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb)
{
auto request = std::make_shared<HttpRequestArgs>();
request->url = url;
request->verb = verb;
return request;
}
bool HttpClient::performRequest(HttpRequestArgsPtr args,
const OnResponseCallback& onResponseCallback)
{
assert(_async && "HttpClient needs its async parameter set to true "
"in order to call performRequest");
if (!_async) return false;
// Enqueue the task
{
// acquire lock
std::unique_lock<std::mutex> lock(_queueMutex);
// add the task
_queue.push(std::make_pair(args, onResponseCallback));
} // release lock
// wake up one thread
_condition.notify_one();
return true;
}
void HttpClient::run()
{
while (true)
{
HttpRequestArgsPtr args;
OnResponseCallback onResponseCallback;
{
std::unique_lock<std::mutex> lock(_queueMutex);
while (!_stop && _queue.empty())
{
_condition.wait(lock);
}
if (_stop) return;
auto p = _queue.front();
_queue.pop();
args = p.first;
onResponseCallback = p.second;
}
if (_stop) return;
HttpResponsePtr response = request(args->url, args->verb, args->body, args);
onResponseCallback(response);
if (_stop) return;
}
}
HttpResponsePtr HttpClient::request(const std::string& url,
const std::string& verb,
const std::string& body,
HttpRequestArgsPtr args,
int redirects)
{
// We only have one socket connection, so we cannot
// make multiple requests concurrently.
std::lock_guard<std::recursive_mutex> lock(_mutex);
uint64_t uploadSize = 0;
uint64_t downloadSize = 0;
int code = 0;
WebSocketHttpHeaders headers;
std::string payload;
std::string description;
std::string protocol, host, path, query;
int port;
bool isProtocolDefaultPort;
if (!UrlParser::parse(url, protocol, host, path, query, port))
if (!UrlParser::parse(url, protocol, host, path, query, port, isProtocolDefaultPort))
{
std::stringstream ss;
ss << "Cannot parse url: " << url;
return std::make_tuple(code, HttpErrorCode::UrlMalformed,
headers, payload, ss.str(),
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::UrlMalformed,
headers,
payload,
ss.str(),
uploadSize,
downloadSize);
}
bool tls = protocol == "https";
std::string errorMsg;
_socket = createSocket(tls, errorMsg);
_socket = createSocket(tls, -1, errorMsg, _tlsOptions);
if (!_socket)
{
return std::make_tuple(code, HttpErrorCode::CannotCreateSocket,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::CannotCreateSocket,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
// Build request string
std::stringstream ss;
ss << verb << " " << path << " HTTP/1.1\r\n";
ss << "Host: " << host << "\r\n";
ss << "User-Agent: ixwebsocket/1.0.0" << "\r\n";
ss << "Accept: */*" << "\r\n";
if (args.compress)
ss << "Host: " << host;
if (!isProtocolDefaultPort)
{
ss << "Accept-Encoding: gzip" << "\r\n";
ss << ":" << port;
}
ss << "\r\n";
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compress && !args->onChunkCallback)
{
ss << "Accept-Encoding: gzip"
<< "\r\n";
}
#endif
// Append extra headers
for (auto&& it : args.extraHeaders)
for (auto&& it : args->extraHeaders)
{
ss << it.first << ": " << it.second << "\r\n";
}
if (verb == kPost)
// Set a default Accept header if none is present
if (args->extraHeaders.find("Accept") == args->extraHeaders.end())
{
ss << "Accept: */*"
<< "\r\n";
}
// Set a default User agent if none is present
if (args->extraHeaders.find("User-Agent") == args->extraHeaders.end())
{
ss << "User-Agent: " << userAgent() << "\r\n";
}
// Set an origin header if missing
if (args->extraHeaders.find("Origin") == args->extraHeaders.end())
{
ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n";
}
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
{
// Set request compression header
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compressRequest)
{
ss << "Content-Encoding: gzip"
<< "\r\n";
}
#endif
ss << "Content-Length: " << body.size() << "\r\n";
// Set default Content-Type if unspecified
if (args.extraHeaders.find("Content-Type") == args.extraHeaders.end())
if (args->extraHeaders.find("Content-Type") == args->extraHeaders.end())
{
ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
if (args->multipartBoundary.empty())
{
ss << "Content-Type: application/x-www-form-urlencoded"
<< "\r\n";
}
else
{
ss << "Content-Type: multipart/form-data; boundary=" << args->multipartBoundary
<< "\r\n";
}
}
ss << "\r\n";
ss << body;
@ -105,35 +252,41 @@ namespace ix
std::string req(ss.str());
std::string errMsg;
std::atomic<bool> requestInitCancellation(false);
// Make a cancellation object dealing with connection timeout
auto isCancellationRequested =
makeCancellationRequestWithTimeout(args.connectTimeout, requestInitCancellation);
auto cancelled = makeCancellationRequestWithTimeout(args->connectTimeout, args->cancel);
auto isCancellationRequested = [&]() {
return cancelled() || _stop;
};
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
if (!success)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::CannotConnect;
std::stringstream ss;
ss << "Cannot connect to url: " << url;
return std::make_tuple(code, HttpErrorCode::CannotConnect,
headers, payload, ss.str(),
uploadSize, downloadSize);
ss << "Cannot connect to url: " << url << " / error : " << errMsg;
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
ss.str(),
uploadSize,
downloadSize);
}
// Make a new cancellation object dealing with transfer timeout
isCancellationRequested =
makeCancellationRequestWithTimeout(args.transferTimeout, requestInitCancellation);
cancelled = makeCancellationRequestWithTimeout(args->transferTimeout, args->cancel);
if (args.verbose)
if (args->verbose)
{
std::stringstream ss;
ss << "Sending " << verb << " request "
<< "to " << host << ":" << port << std::endl
<< "request size: " << req.size() << " bytes" << std::endl
<< "=============" << std::endl
<< req
<< "=============" << std::endl
<< req << "=============" << std::endl
<< std::endl;
log(ss.str(), args);
@ -141,10 +294,16 @@ namespace ix
if (!_socket->writeBytes(req, isCancellationRequested))
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::SendError;
std::string errorMsg("Cannot send request");
return std::make_tuple(code, HttpErrorCode::SendError,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
uploadSize = req.size();
@ -155,13 +314,19 @@ namespace ix
if (!lineValid)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::CannotReadStatusLine;
std::string errorMsg("Cannot retrieve status line");
return std::make_tuple(code, HttpErrorCode::CannotReadStatusLine,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
if (args.verbose)
if (args->verbose)
{
std::stringstream ss;
ss << "Status line " << line;
@ -171,9 +336,14 @@ namespace ix
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
{
std::string errorMsg("Cannot parse response code from status line");
return std::make_tuple(code, HttpErrorCode::MissingStatus,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::MissingStatus,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
auto result = parseHttpHeaders(_socket, isCancellationRequested);
@ -182,42 +352,63 @@ namespace ix
if (!headersValid)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::HeaderParsingError;
std::string errorMsg("Cannot parse http headers");
return std::make_tuple(code, HttpErrorCode::HeaderParsingError,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
// Redirect ?
if ((code >= 301 && code <= 308) && args.followRedirects)
if ((code >= 301 && code <= 308) && args->followRedirects)
{
if (headers.find("Location") == headers.end())
{
std::string errorMsg("Missing location header for redirect");
return std::make_tuple(code, HttpErrorCode::MissingLocation,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::MissingLocation,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
if (redirects >= args.maxRedirects)
if (redirects >= args->maxRedirects)
{
std::stringstream ss;
ss << "Too many redirects: " << redirects;
return std::make_tuple(code, HttpErrorCode::TooManyRedirects,
headers, payload, ss.str(),
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::TooManyRedirects,
headers,
payload,
ss.str(),
uploadSize,
downloadSize);
}
// Recurse
std::string location = headers["Location"];
return request(location, verb, body, args, redirects+1);
return request(location, verb, body, args, redirects + 1);
}
if (verb == "HEAD")
{
return std::make_tuple(code, HttpErrorCode::Ok,
headers, payload, std::string(),
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Ok,
headers,
payload,
std::string(),
uploadSize,
downloadSize);
}
// Parse response:
@ -228,19 +419,29 @@ namespace ix
ss << headers["Content-Length"];
ss >> contentLength;
payload.reserve(contentLength);
auto chunkResult = _socket->readBytes(contentLength,
args.onProgressCallback,
args->onProgressCallback,
args->onChunkCallback,
isCancellationRequested);
if (!chunkResult.first)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
errorMsg = "Cannot read chunk";
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
if (!args->onChunkCallback)
{
payload.reserve(contentLength);
payload += chunkResult.second;
}
payload += chunkResult.second;
}
else if (headers.find("Transfer-Encoding") != headers.end() &&
headers["Transfer-Encoding"] == "chunked")
@ -249,14 +450,20 @@ namespace ix
while (true)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
lineResult = _socket->readLine(isCancellationRequested);
line = lineResult.second;
if (!lineResult.first)
{
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
uint64_t chunkSize;
@ -264,37 +471,52 @@ namespace ix
ss << std::hex << line;
ss >> chunkSize;
if (args.verbose)
if (args->verbose)
{
std::stringstream oss;
oss << "Reading " << chunkSize << " bytes"
<< std::endl;
oss << "Reading " << chunkSize << " bytes" << std::endl;
log(oss.str(), args);
}
payload.reserve(payload.size() + chunkSize);
// Read a chunk
auto chunkResult = _socket->readBytes(chunkSize,
args.onProgressCallback,
auto chunkResult = _socket->readBytes((size_t) chunkSize,
args->onProgressCallback,
args->onChunkCallback,
isCancellationRequested);
if (!chunkResult.first)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
errorMsg = "Cannot read chunk";
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
if (!args->onChunkCallback)
{
payload.reserve(payload.size() + (size_t) chunkSize);
payload += chunkResult.second;
}
payload += chunkResult.second;
// Read the line that terminates the chunk (\r\n)
lineResult = _socket->readLine(isCancellationRequested);
if (!lineResult.first)
{
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
headers, payload, errorMsg,
uploadSize, downloadSize);
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
return std::make_shared<HttpResponse>(code,
description,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
if (chunkSize == 0) break;
@ -307,9 +529,14 @@ namespace ix
else
{
std::string errorMsg("Cannot read http body");
return std::make_tuple(code, HttpErrorCode::CannotReadBody,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::CannotReadBody,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
downloadSize = payload.size();
@ -317,56 +544,141 @@ namespace ix
// If the content was compressed with gzip, decode it
if (headers["Content-Encoding"] == "gzip")
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string decompressedPayload;
if (!gzipInflate(payload, decompressedPayload))
if (!gzipDecompress(payload, decompressedPayload))
{
std::string errorMsg("Error decompressing payload");
return std::make_tuple(code, HttpErrorCode::Gzip,
headers, payload, errorMsg,
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Gzip,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
payload = decompressedPayload;
#else
std::string errorMsg("ixwebsocket was not compiled with gzip support on");
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Gzip,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
#endif
}
return std::make_tuple(code, HttpErrorCode::Ok,
headers, payload, std::string(),
uploadSize, downloadSize);
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Ok,
headers,
payload,
std::string(),
uploadSize,
downloadSize);
}
HttpResponse HttpClient::get(const std::string& url,
const HttpRequestArgs& args)
HttpResponsePtr HttpClient::get(const std::string& url, HttpRequestArgsPtr args)
{
return request(url, kGet, std::string(), args);
}
HttpResponse HttpClient::head(const std::string& url,
const HttpRequestArgs& args)
HttpResponsePtr HttpClient::head(const std::string& url, HttpRequestArgsPtr args)
{
return request(url, kHead, std::string(), args);
}
HttpResponse HttpClient::post(const std::string& url,
const HttpParameters& httpParameters,
const HttpRequestArgs& args)
HttpResponsePtr HttpClient::Delete(const std::string& url, HttpRequestArgsPtr args)
{
return request(url, kPost, serializeHttpParameters(httpParameters), args);
return request(url, kDelete, std::string(), args);
}
HttpResponse HttpClient::post(const std::string& url,
const std::string& body,
const HttpRequestArgs& args)
HttpResponsePtr HttpClient::request(const std::string& url,
const std::string& verb,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
std::string body;
if (httpFormDataParameters.empty())
{
body = serializeHttpParameters(httpParameters);
}
else
{
std::string multipartBoundary = generateMultipartBoundary();
args->multipartBoundary = multipartBoundary;
body = serializeHttpFormDataParameters(
multipartBoundary, httpFormDataParameters, httpParameters);
}
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compressRequest)
{
body = gzipCompress(body);
}
#endif
return request(url, verb, body, args);
}
HttpResponsePtr HttpClient::post(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
return request(url, kPost, httpParameters, httpFormDataParameters, args);
}
HttpResponsePtr HttpClient::post(const std::string& url,
const std::string& body,
HttpRequestArgsPtr args)
{
return request(url, kPost, body, args);
}
HttpResponsePtr HttpClient::put(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
return request(url, kPut, httpParameters, httpFormDataParameters, args);
}
HttpResponsePtr HttpClient::put(const std::string& url,
const std::string& body,
const HttpRequestArgsPtr args)
{
return request(url, kPut, body, args);
}
HttpResponsePtr HttpClient::patch(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
return request(url, kPatch, httpParameters, httpFormDataParameters, args);
}
HttpResponsePtr HttpClient::patch(const std::string& url,
const std::string& body,
const HttpRequestArgsPtr args)
{
return request(url, kPatch, body, args);
}
std::string HttpClient::urlEncode(const std::string& value)
{
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end();
i != n; ++i)
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i)
{
std::string::value_type c = (*i);
@ -394,73 +706,80 @@ namespace ix
for (auto&& it : httpParameters)
{
ss << urlEncode(it.first)
<< "="
<< urlEncode(it.second);
ss << urlEncode(it.first) << "=" << urlEncode(it.second);
if (i++ < (count-1))
if (i++ < (count - 1))
{
ss << "&";
ss << "&";
}
}
return ss.str();
}
bool HttpClient::gzipInflate(
const std::string& in,
std::string& out)
std::string HttpClient::serializeHttpFormDataParameters(
const std::string& multipartBoundary,
const HttpFormDataParameters& httpFormDataParameters,
const HttpParameters& httpParameters)
{
z_stream inflateState;
std::memset(&inflateState, 0, sizeof(inflateState));
//
// --AaB03x
// Content-Disposition: form-data; name="submit-name"
inflateState.zalloc = Z_NULL;
inflateState.zfree = Z_NULL;
inflateState.opaque = Z_NULL;
inflateState.avail_in = 0;
inflateState.next_in = Z_NULL;
// Larry
// --AaB03x
// Content-Disposition: form-data; name="foo.txt"; filename="file1.txt"
// Content-Type: text/plain
if (inflateInit2(&inflateState, 16+MAX_WBITS) != Z_OK)
// ... contents of file1.txt ...
// --AaB03x--
//
std::stringstream ss;
for (auto&& it : httpFormDataParameters)
{
return false;
ss << "--" << multipartBoundary << "\r\n"
<< "Content-Disposition:"
<< " form-data; name=\"" << it.first << "\";"
<< " filename=\"" << it.first << "\""
<< "\r\n"
<< "Content-Type: application/octet-stream"
<< "\r\n"
<< "\r\n"
<< it.second << "\r\n";
}
inflateState.avail_in = (uInt) in.size();
inflateState.next_in = (unsigned char *)(const_cast<char *>(in.data()));
const int kBufferSize = 1 << 14;
std::unique_ptr<unsigned char[]> compressBuffer =
std::make_unique<unsigned char[]>(kBufferSize);
do
for (auto&& it : httpParameters)
{
inflateState.avail_out = (uInt) kBufferSize;
inflateState.next_out = compressBuffer.get();
ss << "--" << multipartBoundary << "\r\n"
<< "Content-Disposition:"
<< " form-data; name=\"" << it.first << "\";"
<< "\r\n"
<< "\r\n"
<< it.second << "\r\n";
}
int ret = inflate(&inflateState, Z_SYNC_FLUSH);
ss << "--" << multipartBoundary << "--\r\n";
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
inflateEnd(&inflateState);
return false;
}
out.append(
reinterpret_cast<char *>(compressBuffer.get()),
kBufferSize - inflateState.avail_out
);
} while (inflateState.avail_out == 0);
inflateEnd(&inflateState);
return true;
return ss.str();
}
void HttpClient::log(const std::string& msg,
const HttpRequestArgs& args)
void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
{
if (args.logger)
if (args->logger)
{
args.logger(msg);
args->logger(msg);
}
}
}
std::string HttpClient::generateMultipartBoundary()
{
std::string str("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
static std::random_device rd;
static std::mt19937 generator(rd());
std::shuffle(str.begin(), str.end(), generator);
return str;
}
} // namespace ix

View File

@ -6,102 +6,118 @@
#pragma once
#include <algorithm>
#include <functional>
#include <mutex>
#include <atomic>
#include <tuple>
#include <memory>
#include <map>
#include "IXHttp.h"
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include "IXWebSocketHttpHeaders.h"
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
namespace ix
{
enum class HttpErrorCode
class HttpClient
{
Ok = 0,
CannotConnect = 1,
Timeout = 2,
Gzip = 3,
UrlMalformed = 4,
CannotCreateSocket = 5,
SendError = 6,
ReadError = 7,
CannotReadStatusLine = 8,
MissingStatus = 9,
HeaderParsingError = 10,
MissingLocation = 11,
TooManyRedirects = 12,
ChunkReadError = 13,
CannotReadBody = 14
};
using HttpResponse = std::tuple<int, // status
HttpErrorCode, // error code
WebSocketHttpHeaders,
std::string, // payload
std::string, // error msg
uint64_t, // upload size
uint64_t>; // download size
using HttpParameters = std::map<std::string, std::string>;
using Logger = std::function<void(const std::string&)>;
struct HttpRequestArgs
{
std::string url;
WebSocketHttpHeaders extraHeaders;
std::string body;
int connectTimeout;
int transferTimeout;
bool followRedirects;
int maxRedirects;
bool verbose;
bool compress;
Logger logger;
OnProgressCallback onProgressCallback;
};
class HttpClient {
public:
HttpClient();
HttpClient(bool async = false);
~HttpClient();
HttpResponse get(const std::string& url,
const HttpRequestArgs& args);
HttpResponse head(const std::string& url,
const HttpRequestArgs& args);
HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr Delete(const std::string& url, HttpRequestArgsPtr args);
HttpResponse post(const std::string& url,
const HttpParameters& httpParameters,
const HttpRequestArgs& args);
HttpResponse post(const std::string& url,
const std::string& body,
const HttpRequestArgs& args);
private:
HttpResponse request(const std::string& url,
const std::string& verb,
HttpResponsePtr post(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr post(const std::string& url,
const std::string& body,
const HttpRequestArgs& args,
int redirects = 0);
HttpRequestArgsPtr args);
HttpResponsePtr put(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr put(const std::string& url,
const std::string& body,
HttpRequestArgsPtr args);
HttpResponsePtr patch(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr patch(const std::string& url,
const std::string& body,
HttpRequestArgsPtr args);
HttpResponsePtr request(const std::string& url,
const std::string& verb,
const std::string& body,
HttpRequestArgsPtr args,
int redirects = 0);
HttpResponsePtr request(const std::string& url,
const std::string& verb,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
void setForceBody(bool value);
// Async API
HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
const std::string& verb = HttpClient::kGet);
bool performRequest(HttpRequestArgsPtr request,
const OnResponseCallback& onResponseCallback);
// TLS
void setTLSOptions(const SocketTLSOptions& tlsOptions);
std::string serializeHttpParameters(const HttpParameters& httpParameters);
std::string serializeHttpFormDataParameters(
const std::string& multipartBoundary,
const HttpFormDataParameters& httpFormDataParameters,
const HttpParameters& httpParameters = HttpParameters());
std::string generateMultipartBoundary();
std::string urlEncode(const std::string& value);
void log(const std::string& msg, const HttpRequestArgs& args);
bool gzipInflate(
const std::string& in,
std::string& out);
std::shared_ptr<Socket> _socket;
const static std::string kPost;
const static std::string kGet;
const static std::string kHead;
const static std::string kDelete;
const static std::string kPut;
const static std::string kPatch;
private:
void log(const std::string& msg, HttpRequestArgsPtr args);
// Async API background thread runner
void run();
// Async API
bool _async;
std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue;
mutable std::mutex _queueMutex;
std::condition_variable _condition;
std::atomic<bool> _stop;
std::thread _thread;
std::unique_ptr<Socket> _socket;
std::recursive_mutex _mutex; // to protect accessing the _socket (only one socket per
// client) the mutex needs to be recursive as this function
// might be called recursively to follow HTTP redirections
SocketTLSOptions _tlsOptions;
bool _forceBody;
};
}
} // namespace ix

View File

@ -0,0 +1,244 @@
/*
* IXHttpServer.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#include "IXHttpServer.h"
#include "IXGzipCodec.h"
#include "IXNetSystem.h"
#include "IXSocketConnect.h"
#include "IXUserAgent.h"
#include <cstdint>
#include <cstring>
#include <fstream>
#include <sstream>
#include <vector>
namespace
{
std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
{
std::vector<uint8_t> memblock;
std::ifstream file(path);
if (!file.is_open()) return std::make_pair(false, memblock);
file.seekg(0, file.end);
std::streamoff size = file.tellg();
file.seekg(0, file.beg);
memblock.resize((size_t) size);
file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
return std::make_pair(true, memblock);
}
std::pair<bool, std::string> readAsString(const std::string& path)
{
auto res = load(path);
auto vec = res.second;
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
}
std::string response_head_file(const std::string& file_name){
if (std::string::npos != file_name.find(".html") || std::string::npos != file_name.find(".htm"))
return "text/html";
else if (std::string::npos != file_name.find(".css"))
return "text/css";
else if (std::string::npos != file_name.find(".js") || std::string::npos != file_name.find(".mjs"))
return "application/x-javascript";
else if (std::string::npos != file_name.find(".ico"))
return "image/x-icon";
else if (std::string::npos != file_name.find(".png"))
return "image/png";
else if (std::string::npos != file_name.find(".jpg") || std::string::npos != file_name.find(".jpeg"))
return "image/jpeg";
else if (std::string::npos != file_name.find(".gif"))
return "image/gif";
else if (std::string::npos != file_name.find(".svg"))
return "image/svg+xml";
else
return "application/octet-stream";
}
} // namespace
namespace ix
{
const int HttpServer::kDefaultTimeoutSecs(30);
HttpServer::HttpServer(int port,
const std::string& host,
int backlog,
size_t maxConnections,
int addressFamily,
int timeoutSecs,
int handshakeTimeoutSecs)
: WebSocketServer(port, host, backlog, maxConnections, handshakeTimeoutSecs, addressFamily)
, _timeoutSecs(timeoutSecs)
{
setDefaultConnectionCallback();
}
void HttpServer::setOnConnectionCallback(const OnConnectionCallback& callback)
{
_onConnectionCallback = callback;
}
void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
std::shared_ptr<ConnectionState> connectionState)
{
auto ret = Http::parseRequest(socket, _timeoutSecs);
// FIXME: handle errors in parseRequest
if (std::get<0>(ret))
{
auto request = std::get<2>(ret);
std::shared_ptr<ix::HttpResponse> response;
if (request->headers["Upgrade"] == "websocket")
{
WebSocketServer::handleUpgrade(std::move(socket), connectionState, request);
}
else
{
auto response = _onConnectionCallback(request, connectionState);
if (!Http::sendResponse(response, socket))
{
logError("Cannot send response");
}
}
}
connectionState->setTerminated();
}
void HttpServer::setDefaultConnectionCallback()
{
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
{
std::string uri(request->uri);
if (uri.empty() || uri == "/")
{
uri = "/index.html";
}
WebSocketHttpHeaders headers;
headers["Server"] = userAgent();
headers["Content-Type"] = response_head_file(uri);
std::string path("." + uri);
auto res = readAsString(path);
bool found = res.first;
if (!found)
{
return std::make_shared<HttpResponse>(
404, "Not Found", HttpErrorCode::Ok, WebSocketHttpHeaders(), std::string());
}
std::string content = res.second;
#ifdef IXWEBSOCKET_USE_ZLIB
std::string acceptEncoding = request->headers["Accept-encoding"];
if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos)
{
content = gzipCompress(content);
headers["Content-Encoding"] = "gzip";
}
headers["Accept-Encoding"] = "gzip";
#endif
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri << " " << content.size();
logInfo(ss.str());
// FIXME: check extensions to set the content type
// headers["Content-Type"] = "application/octet-stream";
headers["Accept-Ranges"] = "none";
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, content);
});
}
void HttpServer::makeRedirectServer(const std::string& redirectUrl)
{
//
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
//
setOnConnectionCallback(
[this, redirectUrl](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
{
WebSocketHttpHeaders headers;
headers["Server"] = userAgent();
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
logInfo(ss.str());
if (request->method == "POST")
{
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, std::string());
}
headers["Location"] = redirectUrl;
return std::make_shared<HttpResponse>(
301, "OK", HttpErrorCode::Ok, headers, std::string());
});
}
//
// Display the client parameter and body on the console
//
void HttpServer::makeDebugServer()
{
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
{
WebSocketHttpHeaders headers;
headers["Server"] = userAgent();
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
logInfo(ss.str());
logInfo("== Headers == ");
for (auto&& it : request->headers)
{
std::ostringstream oss;
oss << it.first << ": " << it.second;
logInfo(oss.str());
}
logInfo("");
logInfo("== Body == ");
logInfo(request->body);
logInfo("");
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, std::string("OK"));
});
}
int HttpServer::getTimeoutSecs()
{
return _timeoutSecs;
}
} // namespace ix

View File

@ -0,0 +1,57 @@
/*
* IXHttpServer.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXHttp.h"
#include "IXWebSocket.h"
#include "IXWebSocketServer.h"
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <utility> // pair
namespace ix
{
class HttpServer final : public WebSocketServer
{
public:
using OnConnectionCallback =
std::function<HttpResponsePtr(HttpRequestPtr, std::shared_ptr<ConnectionState>)>;
HttpServer(int port = SocketServer::kDefaultPort,
const std::string& host = SocketServer::kDefaultHost,
int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections,
int addressFamily = SocketServer::kDefaultAddressFamily,
int timeoutSecs = HttpServer::kDefaultTimeoutSecs,
int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs);
void setOnConnectionCallback(const OnConnectionCallback& callback);
void makeRedirectServer(const std::string& redirectUrl);
void makeDebugServer();
int getTimeoutSecs();
private:
// Member variables
OnConnectionCallback _onConnectionCallback;
const static int kDefaultTimeoutSecs;
int _timeoutSecs;
// Methods
virtual void handleConnection(std::unique_ptr<Socket>,
std::shared_ptr<ConnectionState> connectionState) final;
void setDefaultConnectionCallback();
};
} // namespace ix

View File

@ -5,6 +5,17 @@
*/
#include "IXNetSystem.h"
#include <cstdint>
#include <cstdio>
#ifdef _WIN32
#ifndef EAFNOSUPPORT
#define EAFNOSUPPORT 102
#endif
#ifndef ENOSPC
#define ENOSPC 28
#endif
#include <vector>
#endif
namespace ix
{
@ -15,9 +26,8 @@ namespace ix
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
// Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
return err == 0;
@ -30,10 +40,422 @@ namespace ix
{
#ifdef _WIN32
int err = WSACleanup();
return err == 0;
#else
return true;
#endif
}
}
#ifdef _WIN32
struct WSAEvent
{
public:
WSAEvent(struct pollfd* fd)
: _fd(fd)
{
_event = WSACreateEvent();
}
WSAEvent(WSAEvent&& source) noexcept
{
_event = source._event;
source._event = WSA_INVALID_EVENT; // invalidate the event in the source
_fd = source._fd;
}
~WSAEvent()
{
if (_event != WSA_INVALID_EVENT)
{
// We must deselect the networkevents from the socket event. Otherwise the
// socket will report states that aren't there.
if (_fd != nullptr && (int)_fd->fd != -1)
WSAEventSelect(_fd->fd, _event, 0);
WSACloseEvent(_event);
}
}
operator HANDLE()
{
return _event;
}
operator struct pollfd*()
{
return _fd;
}
private:
HANDLE _event;
struct pollfd* _fd;
};
#endif
//
// That function could 'return WSAPoll(pfd, nfds, timeout);'
// but WSAPoll is said to have weird behaviors on the internet
// (the curl folks have had problems with it).
//
// So we make it a select wrapper
//
// UPDATE: WSAPoll was fixed in Windows 10 Version 2004
//
// The optional "event" is set to nullptr if it wasn't signaled.
int poll(struct pollfd* fds, nfds_t nfds, int timeout, void** event)
{
#ifdef _WIN32
if (event && *event)
{
HANDLE interruptEvent = reinterpret_cast<HANDLE>(*event);
*event = nullptr; // the event wasn't signaled yet
if (nfds < 0 || nfds >= MAXIMUM_WAIT_OBJECTS - 1)
{
WSASetLastError(WSAEINVAL);
return SOCKET_ERROR;
}
std::vector<WSAEvent> socketEvents;
std::vector<HANDLE> handles;
// put the interrupt event as first element, making it highest priority
handles.push_back(interruptEvent);
// create the WSAEvents for the sockets
for (nfds_t i = 0; i < nfds; ++i)
{
struct pollfd* fd = &fds[i];
fd->revents = 0;
if (fd->fd >= 0)
{
// create WSAEvent and add it to the vectors
socketEvents.push_back(std::move(WSAEvent(fd)));
HANDLE handle = socketEvents.back();
if (handle == WSA_INVALID_EVENT)
{
WSASetLastError(WSAENOBUFS);
return SOCKET_ERROR;
}
handles.push_back(handle);
// mapping
long networkEvents = 0;
if (fd->events & (POLLIN )) networkEvents |= FD_READ | FD_ACCEPT;
if (fd->events & (POLLOUT /*| POLLWRNORM | POLLWRBAND*/)) networkEvents |= FD_WRITE | FD_CONNECT;
//if (fd->events & (POLLPRI | POLLRDBAND )) networkEvents |= FD_OOB;
if (WSAEventSelect(fd->fd, handle, networkEvents) != 0)
{
fd->revents = POLLNVAL;
socketEvents.pop_back();
handles.pop_back();
}
}
}
DWORD n = WSAWaitForMultipleEvents(handles.size(), handles.data(), FALSE, timeout != -1 ? static_cast<DWORD>(timeout) : WSA_INFINITE, FALSE);
if (n == WSA_WAIT_FAILED) return SOCKET_ERROR;
if (n == WSA_WAIT_TIMEOUT) return 0;
if (n == WSA_WAIT_EVENT_0)
{
// the interrupt event was signaled
*event = reinterpret_cast<void*>(interruptEvent);
return 1;
}
int handleIndex = n - WSA_WAIT_EVENT_0;
int socketIndex = handleIndex - 1;
WSANETWORKEVENTS netEvents;
int count = 0;
// WSAWaitForMultipleEvents returns the index of the first signaled event. And to emulate WSAPoll()
// all the signaled events must be processed.
while (socketIndex < (int)socketEvents.size())
{
struct pollfd* fd = socketEvents[socketIndex];
memset(&netEvents, 0, sizeof(netEvents));
if (WSAEnumNetworkEvents(fd->fd, socketEvents[socketIndex], &netEvents) != 0)
{
fd->revents = POLLERR;
}
else if (netEvents.lNetworkEvents != 0)
{
// mapping
if (netEvents.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_OOB)) fd->revents |= POLLIN;
if (netEvents.lNetworkEvents & (FD_WRITE | FD_CONNECT )) fd->revents |= POLLOUT;
for (int i = 0; i < FD_MAX_EVENTS; ++i)
{
if (netEvents.iErrorCode[i] != 0)
{
fd->revents |= POLLERR;
break;
}
}
if (fd->revents != 0)
{
// only signaled sockets count
count++;
}
}
socketIndex++;
}
return count;
}
else
{
if (event && *event) *event = nullptr;
socket_t maxfd = 0;
fd_set readfds, writefds, errorfds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&errorfds);
for (nfds_t i = 0; i < nfds; ++i)
{
struct pollfd* fd = &fds[i];
if (fd->fd > maxfd)
{
maxfd = fd->fd;
}
if ((fd->events & POLLIN))
{
FD_SET(fd->fd, &readfds);
}
if ((fd->events & POLLOUT))
{
FD_SET(fd->fd, &writefds);
}
if ((fd->events & POLLERR))
{
FD_SET(fd->fd, &errorfds);
}
}
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
int ret = select(maxfd + 1, &readfds, &writefds, &errorfds, timeout != -1 ? &tv : NULL);
if (ret < 0)
{
return ret;
}
for (nfds_t i = 0; i < nfds; ++i)
{
struct pollfd* fd = &fds[i];
fd->revents = 0;
if (FD_ISSET(fd->fd, &readfds))
{
fd->revents |= POLLIN;
}
if (FD_ISSET(fd->fd, &writefds))
{
fd->revents |= POLLOUT;
}
if (FD_ISSET(fd->fd, &errorfds))
{
fd->revents |= POLLERR;
}
}
return ret;
}
#else
if (event && *event) *event = nullptr;
//
// It was reported that on Android poll can fail and return -1 with
// errno == EINTR, which should be a temp error and should typically
// be handled by retrying in a loop.
// Maybe we need to put all syscall / C functions in
// a new IXSysCalls.cpp and wrap them all.
//
// The style from libuv is as such.
//
int ret = -1;
do
{
ret = ::poll(fds, nfds, timeout);
} while (ret == -1 && errno == EINTR);
return ret;
#endif
}
//
// mingw does not have inet_ntop, which were taken as is from the musl C library.
//
const char* inet_ntop(int af, const void* a0, char* s, socklen_t l)
{
#if defined(_WIN32) && defined(__GNUC__)
const unsigned char* a = (const unsigned char*) a0;
int i, j, max, best;
char buf[100];
switch (af)
{
case AF_INET:
if (snprintf(s, l, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]) < l) return s;
break;
case AF_INET6:
if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\377\377", 12))
snprintf(buf,
sizeof buf,
"%x:%x:%x:%x:%x:%x:%x:%x",
256 * a[0] + a[1],
256 * a[2] + a[3],
256 * a[4] + a[5],
256 * a[6] + a[7],
256 * a[8] + a[9],
256 * a[10] + a[11],
256 * a[12] + a[13],
256 * a[14] + a[15]);
else
snprintf(buf,
sizeof buf,
"%x:%x:%x:%x:%x:%x:%d.%d.%d.%d",
256 * a[0] + a[1],
256 * a[2] + a[3],
256 * a[4] + a[5],
256 * a[6] + a[7],
256 * a[8] + a[9],
256 * a[10] + a[11],
a[12],
a[13],
a[14],
a[15]);
/* Replace longest /(^0|:)[:0]{2,}/ with "::" */
for (i = best = 0, max = 2; buf[i]; i++)
{
if (i && buf[i] != ':') continue;
j = strspn(buf + i, ":0");
if (j > max) best = i, max = j;
}
if (max > 3)
{
buf[best] = buf[best + 1] = ':';
memmove(buf + best + 2, buf + best + max, i - best - max + 1);
}
if (strlen(buf) < (size_t)l)
{
strcpy(s, buf);
return s;
}
break;
default: errno = EAFNOSUPPORT; return 0;
}
errno = ENOSPC;
return 0;
#else
return ::inet_ntop(af, a0, s, l);
#endif
}
#if defined(_WIN32) && defined(__GNUC__)
static int hexval(unsigned c)
{
if (c - '0' < 10) return c - '0';
c |= 32;
if (c - 'a' < 6) return c - 'a' + 10;
return -1;
}
#endif
//
// mingw does not have inet_pton, which were taken as is from the musl C library.
//
int inet_pton(int af, const char* s, void* a0)
{
#if defined(_WIN32) && defined(__GNUC__)
uint16_t ip[8];
unsigned char* a = (unsigned char*) a0;
int i, j, v, d, brk = -1, need_v4 = 0;
if (af == AF_INET)
{
for (i = 0; i < 4; i++)
{
for (v = j = 0; j < 3 && isdigit(s[j]); j++)
v = 10 * v + s[j] - '0';
if (j == 0 || (j > 1 && s[0] == '0') || v > 255) return 0;
a[i] = v;
if (s[j] == 0 && i == 3) return 1;
if (s[j] != '.') return 0;
s += j + 1;
}
return 0;
}
else if (af != AF_INET6)
{
errno = EAFNOSUPPORT;
return -1;
}
if (*s == ':' && *++s != ':') return 0;
for (i = 0;; i++)
{
if (s[0] == ':' && brk < 0)
{
brk = i;
ip[i & 7] = 0;
if (!*++s) break;
if (i == 7) return 0;
continue;
}
for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++)
v = 16 * v + d;
if (j == 0) return 0;
ip[i & 7] = v;
if (!s[j] && (brk >= 0 || i == 7)) break;
if (i == 7) return 0;
if (s[j] != ':')
{
if (s[j] != '.' || (i < 6 && brk < 0)) return 0;
need_v4 = 1;
i++;
break;
}
s += j + 1;
}
if (brk >= 0)
{
memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk));
for (j = 0; j < 7 - i; j++)
ip[brk + j] = 0;
}
for (j = 0; j < 8; j++)
{
*a++ = ip[j] >> 8;
*a++ = ip[j];
}
if (need_v4 && inet_pton(AF_INET, (const char*) s, a - 4) <= 0) return 0;
return 1;
#else
return ::inet_pton(af, s, a0);
#endif
}
// Convert network bytes to host bytes. Copied from the ASIO library
unsigned short network_to_host_short(unsigned short value)
{
#if defined(_WIN32)
unsigned char* value_p = reinterpret_cast<unsigned char*>(&value);
unsigned short result = (static_cast<unsigned short>(value_p[0]) << 8)
| static_cast<unsigned short>(value_p[1]);
return result;
#else // defined(_WIN32)
return ntohs(value);
#endif // defined(_WIN32)
}
} // namespace ix

View File

@ -6,26 +6,88 @@
#pragma once
#include <cstdint>
#ifdef __FreeBSD__
#include <sys/types.h>
#endif
#ifdef _WIN32
# include <WS2tcpip.h>
# include <WinSock2.h>
# include <basetsd.h>
# include <io.h>
# include <ws2def.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <ws2tcpip.h>
#include <winsock2.h>
#include <basetsd.h>
#include <io.h>
#include <ws2def.h>
#include <cerrno>
#undef EWOULDBLOCK
#undef EAGAIN
#undef EINPROGRESS
#undef EBADF
#undef EINVAL
// map to WSA error codes
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EAGAIN WSATRY_AGAIN
#define EINPROGRESS WSAEINPROGRESS
#define EBADF WSAEBADF
#define EINVAL WSAEINVAL
// Define our own poll on Windows, as a wrapper on top of select
typedef unsigned long int nfds_t;
// pollfd is not defined by some versions of mingw64 since _WIN32_WINNT is too low
#if _WIN32_WINNT < 0x0600
struct pollfd
{
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
#define POLLIN 0x001 /* There is data to read. */
#define POLLOUT 0x004 /* Writing now will not block. */
#define POLLERR 0x008 /* Error condition. */
#define POLLHUP 0x010 /* Hung up. */
#define POLLNVAL 0x020 /* Invalid polling request. */
#endif
#else
# include <arpa/inet.h>
# include <errno.h>
# include <netdb.h>
# include <netinet/tcp.h>
# include <sys/select.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/time.h>
# include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#endif
namespace ix
{
#ifdef _WIN32
typedef SOCKET socket_t;
#else
typedef int socket_t;
#endif
bool initNetSystem();
bool uninitNetSystem();
}
int poll(struct pollfd* fds, nfds_t nfds, int timeout, void** event);
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
int inet_pton(int af, const char* src, void* dst);
unsigned short network_to_host_short(unsigned short value);
} // namespace ix

View File

@ -7,8 +7,10 @@
#pragma once
#include <functional>
#include <string>
namespace ix
{
using OnProgressCallback = std::function<bool(int current, int total)>;
using OnChunkCallback = std::function<void(const std::string&)>;
}

View File

@ -8,6 +8,9 @@
namespace ix
{
const uint64_t SelectInterrupt::kSendRequest = 1;
const uint64_t SelectInterrupt::kCloseRequest = 2;
SelectInterrupt::SelectInterrupt()
{
;
@ -42,5 +45,9 @@ namespace ix
{
return -1;
}
}
void* SelectInterrupt::getEvent() const
{
return nullptr;
}
} // namespace ix

View File

@ -6,12 +6,14 @@
#pragma once
#include <stdint.h>
#include <cstdint>
#include <memory>
#include <string>
namespace ix
{
class SelectInterrupt {
class SelectInterrupt
{
public:
SelectInterrupt();
virtual ~SelectInterrupt();
@ -22,7 +24,12 @@ namespace ix
virtual bool clear();
virtual uint64_t read();
virtual int getFd() const;
virtual void* getEvent() const;
// Used as special codes for pipe communication
static const uint64_t kSendRequest;
static const uint64_t kCloseRequest;
};
}
using SelectInterruptPtr = std::unique_ptr<SelectInterrupt>;
} // namespace ix

View File

@ -0,0 +1,85 @@
/*
* IXSelectInterruptEvent.cpp
*/
//
// On Windows we use a Windows Event to wake up ix::poll() (WSAWaitForMultipleEvents).
// And on any other platform that doesn't support pipe file descriptors we
// emulate the interrupt event by using a short timeout with ix::poll() and
// read from the SelectInterrupt. (see Socket::poll() "Emulation mode")
//
#include <algorithm>
#include "IXSelectInterruptEvent.h"
namespace ix
{
SelectInterruptEvent::SelectInterruptEvent()
{
#ifdef _WIN32
_event = CreateEvent(NULL, TRUE, FALSE, NULL);
#endif
}
SelectInterruptEvent::~SelectInterruptEvent()
{
#ifdef _WIN32
CloseHandle(_event);
#endif
}
bool SelectInterruptEvent::init(std::string& /*errorMsg*/)
{
return true;
}
bool SelectInterruptEvent::notify(uint64_t value)
{
std::lock_guard<std::mutex> lock(_valuesMutex);
// WebSocket implementation detail: We only need one of the values in the queue
if (std::find(_values.begin(), _values.end(), value) == _values.end())
_values.push_back(value);
#ifdef _WIN32
SetEvent(_event); // wake up
#endif
return true;
}
uint64_t SelectInterruptEvent::read()
{
std::lock_guard<std::mutex> lock(_valuesMutex);
if (_values.size() > 0)
{
uint64_t value = _values.front();
_values.pop_front();
#ifdef _WIN32
// signal the event if there is still data in the queue
if (_values.size() == 0)
ResetEvent(_event);
#endif
return value;
}
return 0;
}
bool SelectInterruptEvent::clear()
{
std::lock_guard<std::mutex> lock(_valuesMutex);
_values.clear();
#ifdef _WIN32
ResetEvent(_event);
#endif
return true;
}
void* SelectInterruptEvent::getEvent() const
{
#ifdef _WIN32
return reinterpret_cast<void*>(_event);
#else
return nullptr;
#endif
}
} // namespace ix

View File

@ -0,0 +1,39 @@
/*
* IXSelectInterruptEvent.h
*/
#pragma once
#include "IXSelectInterrupt.h"
#include <cstdint>
#include <mutex>
#include <string>
#include <deque>
#ifdef _WIN32
#include <windows.h>
#endif
namespace ix
{
class SelectInterruptEvent final : public SelectInterrupt
{
public:
SelectInterruptEvent();
virtual ~SelectInterruptEvent();
bool init(std::string& /*errorMsg*/) final;
bool notify(uint64_t value) final;
bool clear() final;
uint64_t read() final;
void* getEvent() const final;
private:
// contains every value only once, new values are inserted at the begin, nu
std::deque<uint64_t> _values;
std::mutex _valuesMutex;
#ifdef _WIN32
// Windows Event to wake up the socket poll
HANDLE _event;
#endif
};
} // namespace ix

View File

@ -1,116 +0,0 @@
/*
* IXSelectInterruptEventFd.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
*/
//
// On Linux we use eventd to wake up select.
//
//
// Linux/Android has a special type of virtual files. select(2) will react
// when reading/writing to those files, unlike closing sockets.
//
// https://linux.die.net/man/2/eventfd
// http://www.sourcexr.com/articles/2013/10/26/lightweight-inter-process-signaling-with-eventfd
//
// eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4)
// is on Kernel 3.x
//
// cf Android/Kernel table here
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
//
// On macOS we use UNIX pipes to wake up select.
//
#include "IXSelectInterruptEventFd.h"
#include <sys/eventfd.h>
#include <unistd.h> // for write
#include <string.h> // for strerror
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <sstream>
namespace ix
{
SelectInterruptEventFd::SelectInterruptEventFd()
{
_eventfd = -1;
}
SelectInterruptEventFd::~SelectInterruptEventFd()
{
::close(_eventfd);
}
bool SelectInterruptEventFd::init(std::string& errorMsg)
{
// calling init twice is a programming error
assert(_eventfd == -1);
_eventfd = eventfd(0, 0);
if (_eventfd < 0)
{
std::stringstream ss;
ss << "SelectInterruptEventFd::init() failed in eventfd()"
<< " : " << strerror(errno);
errorMsg = ss.str();
_eventfd = -1;
return false;
}
if (fcntl(_eventfd, F_SETFL, O_NONBLOCK) == -1)
{
std::stringstream ss;
ss << "SelectInterruptEventFd::init() failed in fcntl() call"
<< " : " << strerror(errno);
errorMsg = ss.str();
_eventfd = -1;
return false;
}
return true;
}
bool SelectInterruptEventFd::notify(uint64_t value)
{
int fd = _eventfd;
if (fd == -1) return false;
// we should write 8 bytes for an uint64_t
return write(fd, &value, sizeof(value)) == 8;
}
// TODO: return max uint64_t for errors ?
uint64_t SelectInterruptEventFd::read()
{
int fd = _eventfd;
uint64_t value = 0;
::read(fd, &value, sizeof(value));
return value;
}
bool SelectInterruptEventFd::clear()
{
if (_eventfd == -1) return false;
// 0 is a special value ; select will not wake up
uint64_t value = 0;
// we should write 8 bytes for an uint64_t
return write(_eventfd, &value, sizeof(value)) == 8;
}
int SelectInterruptEventFd::getFd() const
{
return _eventfd;
}
}

View File

@ -1,32 +0,0 @@
/*
* IXSelectInterruptEventFd.h
* Author: Benjamin Sergeant
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSelectInterrupt.h"
#include <stdint.h>
#include <string>
namespace ix
{
class SelectInterruptEventFd final : public SelectInterrupt {
public:
SelectInterruptEventFd();
virtual ~SelectInterruptEventFd();
bool init(std::string& errorMsg) final;
bool notify(uint64_t value) final;
bool clear() final;
uint64_t read() final;
int getFd() const final;
private:
int _eventfd;
};
}

View File

@ -6,20 +6,21 @@
#include "IXSelectInterruptFactory.h"
#if defined(__linux__) || defined(__APPLE__)
# include <ixwebsocket/IXSelectInterruptPipe.h>
#include "IXUniquePtr.h"
#if _WIN32
#include "IXSelectInterruptEvent.h"
#else
# include <ixwebsocket/IXSelectInterrupt.h>
#include "IXSelectInterruptPipe.h"
#endif
namespace ix
{
std::shared_ptr<SelectInterrupt> createSelectInterrupt()
SelectInterruptPtr createSelectInterrupt()
{
#if defined(__linux__) || defined(__APPLE__)
return std::make_shared<SelectInterruptPipe>();
#ifdef _WIN32
return ix::make_unique<SelectInterruptEvent>();
#else
return std::make_shared<SelectInterrupt>();
return ix::make_unique<SelectInterruptPipe>();
#endif
}
}
} // namespace ix

View File

@ -11,5 +11,6 @@
namespace ix
{
class SelectInterrupt;
std::shared_ptr<SelectInterrupt> createSelectInterrupt();
}
using SelectInterruptPtr = std::unique_ptr<SelectInterrupt>;
SelectInterruptPtr createSelectInterrupt();
} // namespace ix

View File

@ -5,17 +5,19 @@
*/
//
// On macOS we use UNIX pipes to wake up select.
// On UNIX we use pipes to wake up select. There is no way to do that
// on Windows so this file is compiled out on Windows.
//
#ifndef _WIN32
#include "IXSelectInterruptPipe.h"
#include <unistd.h> // for write
#include <string.h> // for strerror
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sstream>
#include <string.h> // for strerror
#include <unistd.h> // for write
namespace ix
{
@ -32,8 +34,12 @@ namespace ix
SelectInterruptPipe::~SelectInterruptPipe()
{
::close(_fildes[kPipeReadIndex]);
::close(_fildes[kPipeWriteIndex]);
if (-1 != _fildes[kPipeReadIndex]) {
::close(_fildes[kPipeReadIndex]);
}
if (-1 != _fildes[kPipeWriteIndex]) {
::close(_fildes[kPipeWriteIndex]);
}
_fildes[kPipeReadIndex] = -1;
_fildes[kPipeWriteIndex] = -1;
}
@ -115,8 +121,14 @@ namespace ix
int fd = _fildes[kPipeWriteIndex];
if (fd == -1) return false;
ssize_t ret = -1;
do
{
ret = ::write(fd, &value, sizeof(value));
} while (ret == -1 && errno == EINTR);
// we should write 8 bytes for an uint64_t
return write(fd, &value, sizeof(value)) == 8;
return ret == 8;
}
// TODO: return max uint64_t for errors ?
@ -127,7 +139,12 @@ namespace ix
int fd = _fildes[kPipeReadIndex];
uint64_t value = 0;
::read(fd, &value, sizeof(value));
ssize_t ret = -1;
do
{
ret = ::read(fd, &value, sizeof(value));
} while (ret == -1 && errno == EINTR);
return value;
}
@ -143,4 +160,6 @@ namespace ix
return _fildes[kPipeReadIndex];
}
}
} // namespace ix
#endif // !_WIN32

View File

@ -7,14 +7,15 @@
#pragma once
#include "IXSelectInterrupt.h"
#include <cstdint>
#include <mutex>
#include <stdint.h>
#include <string>
#include <mutex>
namespace ix
{
class SelectInterruptPipe final : public SelectInterrupt {
class SelectInterruptPipe final : public SelectInterrupt
{
public:
SelectInterruptPipe();
virtual ~SelectInterruptPipe();
@ -37,5 +38,4 @@ namespace ix
static const int kPipeReadIndex;
static const int kPipeWriteIndex;
};
}
} // namespace ix

View File

@ -0,0 +1,87 @@
/*
* IXSetThreadName.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXSetThreadName.h"
// unix systems
#if defined(__APPLE__) || defined(__linux__) || defined(BSD)
#include <pthread.h>
#endif
// freebsd needs this header as well
#if defined(BSD)
#include <pthread_np.h>
#endif
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif
// Windows
#ifdef _WIN32
#include <windows.h>
#endif
namespace ix
{
#ifdef _WIN32
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetThreadName(DWORD dwThreadID, const char* threadName)
{
#ifndef __GNUC__
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
{
RaiseException(
MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
#endif
}
#endif
void setThreadName(const std::string& name)
{
#if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
//
// Apple reserves 16 bytes for its thread names
// Notice that the Apple version of pthread_setname_np
// does not take a pthread_t argument
//
pthread_setname_np(name.substr(0, 63).c_str());
#elif defined(__linux__)
//
// Linux only reserves 16 bytes for its thread names
// See prctl and PR_SET_NAME property in
// http://man7.org/linux/man-pages/man2/prctl.2.html
//
pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
#elif defined(_WIN32)
SetThreadName(-1, name.c_str());
#elif defined(BSD)
pthread_set_name_np(pthread_self(), name.substr(0, 15).c_str());
#else
// ... assert here ?
#endif
}
} // namespace ix

View File

@ -10,4 +10,3 @@ namespace ix
{
void setThreadName(const std::string& name);
}

View File

@ -5,20 +5,20 @@
*/
#include "IXSocket.h"
#include "IXSocketConnect.h"
#include "IXNetSystem.h"
#include "IXSelectInterrupt.h"
#include "IXSelectInterruptFactory.h"
#include "IXSocketConnect.h"
#include <algorithm>
#include <array>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/types.h>
#include <algorithm>
#include <vector>
#ifdef min
#undef min
@ -28,13 +28,10 @@ namespace ix
{
const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
const uint64_t Socket::kSendRequest = 1;
const uint64_t Socket::kCloseRequest = 2;
constexpr size_t Socket::kChunkSize;
Socket::Socket(int fd) :
_sockfd(fd),
_selectInterrupt(createSelectInterrupt())
Socket::Socket(int fd)
: _sockfd(fd)
, _selectInterrupt(createSelectInterrupt())
{
;
}
@ -44,45 +41,61 @@ namespace ix
close();
}
PollResultType Socket::poll(int timeoutMs)
PollResultType Socket::poll(bool readyToRead,
int timeoutMs,
int sockfd,
const SelectInterruptPtr& selectInterrupt)
{
if (_sockfd == -1)
{
return PollResultType::Error;
}
PollResultType pollResult = PollResultType::ReadyForRead;
return isReadyToRead(timeoutMs);
}
//
// 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
// to ::poll does fix that.
//
// However poll isn't as portable as select and has bugs on Windows, so we
// have a shim to fallback to select on those platforms. See
// https://github.com/mpv-player/mpv/pull/5203/files for such a select wrapper.
//
nfds_t nfds = 1;
struct pollfd fds[2];
memset(fds, 0, sizeof(fds));
PollResultType Socket::select(bool readyToRead, int timeoutMs)
{
fd_set rfds;
fd_set wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
fds[0].fd = sockfd;
fds[0].events = (readyToRead) ? POLLIN : POLLOUT;
fd_set* fds = (readyToRead) ? &rfds : & wfds;
FD_SET(_sockfd, fds);
// this is ignored by poll, but our select based poll wrapper on Windows needs it
fds[0].events |= POLLERR;
// File descriptor used to interrupt select when needed
int interruptFd = _selectInterrupt->getFd();
if (interruptFd != -1)
int interruptFd = -1;
void* interruptEvent = nullptr;
if (selectInterrupt)
{
FD_SET(interruptFd, fds);
interruptFd = selectInterrupt->getFd();
interruptEvent = selectInterrupt->getEvent();
if (interruptFd != -1)
{
nfds = 2;
fds[1].fd = interruptFd;
fds[1].events = POLLIN;
}
else if (interruptEvent == nullptr)
{
// Emulation mode: SelectInterrupt neither supports file descriptors nor events
// Check the selectInterrupt for requests before doing the poll().
if (readSelectInterruptRequest(selectInterrupt, &pollResult))
{
return pollResult;
}
}
}
struct timeval timeout;
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = 1000 * (timeoutMs % 1000);
void* event = interruptEvent; // ix::poll will set event to nullptr if it wasn't signaled
int ret = ix::poll(fds, nfds, timeoutMs, &event);
// Compute the highest fd.
int sockfd = _sockfd;
int nfds = (std::max)(sockfd, interruptFd);
int ret = ::select(nfds + 1, &rfds, &wfds, nullptr,
(timeoutMs < 0) ? nullptr : &timeout);
PollResultType pollResult = PollResultType::ReadyForRead;
if (ret < 0)
{
pollResult = PollResultType::Error;
@ -90,50 +103,120 @@ namespace ix
else if (ret == 0)
{
pollResult = PollResultType::Timeout;
}
else if (interruptFd != -1 && FD_ISSET(interruptFd, &rfds))
{
uint64_t value = _selectInterrupt->read();
if (selectInterrupt && interruptFd == -1 && interruptEvent == nullptr)
{
// Emulation mode: SelectInterrupt neither supports fd nor events
if (value == kSendRequest)
{
pollResult = PollResultType::SendRequest;
}
else if (value == kCloseRequest)
{
pollResult = PollResultType::CloseRequest;
// Check the selectInterrupt for requests
readSelectInterruptRequest(selectInterrupt, &pollResult);
}
}
else if (sockfd != -1 && readyToRead && FD_ISSET(sockfd, &rfds))
else if ((interruptFd != -1 && fds[1].revents & POLLIN) || (interruptEvent != nullptr && event != nullptr))
{
// The InterruptEvent was signaled
readSelectInterruptRequest(selectInterrupt, &pollResult);
}
else if (sockfd != -1 && readyToRead && fds[0].revents & POLLIN)
{
pollResult = PollResultType::ReadyForRead;
}
else if (sockfd != -1 && !readyToRead && FD_ISSET(sockfd, &wfds))
else if (sockfd != -1 && !readyToRead && fds[0].revents & POLLOUT)
{
pollResult = PollResultType::ReadyForWrite;
#ifdef _WIN32
// On connect error, in async mode, windows will write to the exceptions fds
if (fds[0].revents & POLLERR)
{
pollResult = PollResultType::Error;
}
#else
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;
}
#endif
}
else if (sockfd != -1 && (fds[0].revents & POLLERR || fds[0].revents & POLLHUP ||
fds[0].revents & POLLNVAL))
{
pollResult = PollResultType::Error;
}
return pollResult;
}
bool Socket::readSelectInterruptRequest(const SelectInterruptPtr& selectInterrupt,
PollResultType* pollResult)
{
uint64_t value = selectInterrupt->read();
if (value == SelectInterrupt::kSendRequest)
{
*pollResult = PollResultType::SendRequest;
return true;
}
else if (value == SelectInterrupt::kCloseRequest)
{
*pollResult = PollResultType::CloseRequest;
return true;
}
return false;
}
PollResultType Socket::isReadyToRead(int timeoutMs)
{
if (_sockfd == -1)
{
return PollResultType::Error;
}
bool readyToRead = true;
return select(readyToRead, timeoutMs);
return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt);
}
PollResultType Socket::isReadyToWrite(int timeoutMs)
{
if (_sockfd == -1)
{
return PollResultType::Error;
}
bool readyToRead = false;
return select(readyToRead, timeoutMs);
return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt);
}
// Wake up from poll/select by writing to the pipe which is watched by select
bool Socket::wakeUpFromPoll(uint8_t wakeUpCode)
bool Socket::wakeUpFromPoll(uint64_t wakeUpCode)
{
return _selectInterrupt->notify(wakeUpCode);
}
bool Socket::isWakeUpFromPollSupported()
{
return _selectInterrupt->getFd() != -1 || _selectInterrupt->getEvent() != nullptr;
}
bool Socket::accept(std::string& errMsg)
{
if (_sockfd == -1)
{
errMsg = "Socket is uninitialized";
return false;
}
return true;
}
bool Socket::connect(const std::string& host,
int port,
std::string& errMsg,
@ -169,7 +252,7 @@ namespace ix
ssize_t Socket::send(const std::string& buffer)
{
return send((char*)&buffer[0], buffer.size());
return send((char*) &buffer[0], buffer.size());
}
ssize_t Socket::recv(void* buffer, size_t length)
@ -224,19 +307,28 @@ namespace ix
bool Socket::writeBytes(const std::string& str,
const CancellationRequest& isCancellationRequested)
{
int offset = 0;
int len = (int) str.size();
while (true)
{
if (isCancellationRequested && isCancellationRequested()) return false;
char* buffer = const_cast<char*>(str.c_str());
int len = (int) str.size();
ssize_t ret = send(buffer, len);
ssize_t ret = send((char*) &str[offset], len);
// We wrote some bytes, as needed, all good.
if (ret > 0)
{
return ret == len;
if (ret == len)
{
return true;
}
else
{
offset += ret;
len -= ret;
continue;
}
}
// There is possibly something to be writen, try again
else if (ret < 0 && Socket::isWaitNeeded())
@ -251,8 +343,7 @@ namespace ix
}
}
bool Socket::readByte(void* buffer,
const CancellationRequest& isCancellationRequested)
bool Socket::readByte(void* buffer, const CancellationRequest& isCancellationRequested)
{
while (true)
{
@ -291,7 +382,7 @@ namespace ix
std::string line;
line.reserve(64);
for (int i = 0; i < 2 || (line[i-2] != '\r' && line[i-1] != '\n'); ++i)
for (int i = 0; i < 2 || (line[i - 2] != '\r' && line[i - 1] != '\n'); ++i)
{
if (!readByte(&c, isCancellationRequested))
{
@ -308,47 +399,54 @@ namespace ix
std::pair<bool, std::string> Socket::readBytes(
size_t length,
const OnProgressCallback& onProgressCallback,
const OnChunkCallback& onChunkCallback,
const CancellationRequest& isCancellationRequested)
{
if (_readBuffer.empty())
{
_readBuffer.resize(kChunkSize);
}
std::array<uint8_t, 1 << 14> readBuffer;
std::vector<uint8_t> output;
while (output.size() != length)
size_t bytesRead = 0;
while (bytesRead != length)
{
if (isCancellationRequested && isCancellationRequested())
{
return std::make_pair(false, std::string());
const std::string errorMsg("Cancellation Requested");
return std::make_pair(false, errorMsg);
}
size_t size = std::min(kChunkSize, length - output.size());
ssize_t ret = recv((char*)&_readBuffer[0], size);
size_t size = std::min(readBuffer.size(), length - bytesRead);
ssize_t ret = recv((char*) &readBuffer[0], size);
if (ret <= 0 && !Socket::isWaitNeeded())
if (ret > 0)
{
// Error
return std::make_pair(false, std::string());
if (onChunkCallback)
{
std::string chunk(readBuffer.begin(), readBuffer.begin() + ret);
onChunkCallback(chunk);
}
else
{
output.insert(output.end(), readBuffer.begin(), readBuffer.begin() + ret);
}
bytesRead += ret;
}
else
else if (ret <= 0 && !Socket::isWaitNeeded())
{
output.insert(output.end(),
_readBuffer.begin(),
_readBuffer.begin() + ret);
const std::string errorMsg("Recv Error");
return std::make_pair(false, errorMsg);
}
if (onProgressCallback) onProgressCallback((int) output.size(), (int) length);
if (onProgressCallback) onProgressCallback((int) bytesRead, (int) length);
// Wait with a 1ms timeout until the socket is ready to read.
// This way we are not busy looping
if (isReadyToRead(1) == PollResultType::Error)
{
return std::make_pair(false, std::string());
const std::string errorMsg("Poll Error");
return std::make_pair(false, errorMsg);
}
}
return std::make_pair(true, std::string(output.begin(),
output.end()));
return std::make_pair(true, std::string(output.begin(), output.end()));
}
}
} // namespace ix

View File

@ -6,50 +6,42 @@
#pragma once
#include <string>
#include <functional>
#include <mutex>
#include <atomic>
#include <vector>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#ifdef __APPLE__
#include <sys/types.h>
#endif
#ifdef _WIN32
#include <BaseTsd.h>
#include <basetsd.h>
#ifdef _MSC_VER
typedef SSIZE_T ssize_t;
#undef EWOULDBLOCK
#undef EAGAIN
#undef EINPROGRESS
#undef EBADF
#undef EINVAL
// map to WSA error codes
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EAGAIN WSATRY_AGAIN
#define EINPROGRESS WSAEINPROGRESS
#define EBADF WSAEBADF
#define EINVAL WSAEINVAL
#endif
#endif
#include "IXCancellationRequest.h"
#include "IXProgressCallback.h"
#include "IXSelectInterrupt.h"
namespace ix
{
class SelectInterrupt;
enum class PollResultType
{
ReadyForRead = 0,
ReadyForWrite = 1,
Timeout = 2,
Error = 3,
SendRequest = 4,
CloseRequest = 5
ReadyForRead = 0,
ReadyForWrite = 1,
Timeout = 2,
Error = 3,
SendRequest = 4,
CloseRequest = 5
};
class Socket {
class Socket
{
public:
Socket(int fd = -1);
virtual ~Socket();
@ -57,58 +49,56 @@ namespace ix
// Functions to check whether there is activity on the socket
PollResultType poll(int timeoutMs = kDefaultPollTimeout);
bool wakeUpFromPoll(uint8_t wakeUpCode);
bool wakeUpFromPoll(uint64_t wakeUpCode);
bool isWakeUpFromPollSupported();
PollResultType isReadyToWrite(int timeoutMs);
PollResultType isReadyToRead(int timeoutMs);
// Virtual methods
virtual bool connect(const std::string& url,
virtual bool accept(std::string& errMsg);
virtual bool connect(const std::string& host,
int port,
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
virtual void close();
virtual ssize_t send(char* buffer, size_t length);
virtual ssize_t send(const std::string& buffer);
ssize_t send(const std::string& buffer);
virtual ssize_t recv(void* buffer, size_t length);
// Blocking and cancellable versions, working with socket that can be set
// to non blocking mode. Used during HTTP upgrade.
bool readByte(void* buffer,
const CancellationRequest& isCancellationRequested);
bool writeBytes(const std::string& str,
const CancellationRequest& isCancellationRequested);
bool readByte(void* buffer, const CancellationRequest& isCancellationRequested);
bool writeBytes(const std::string& str, const CancellationRequest& isCancellationRequested);
std::pair<bool, std::string> readLine(
const CancellationRequest& isCancellationRequested);
std::pair<bool, std::string> readBytes(
size_t length,
const OnProgressCallback& onProgressCallback,
const CancellationRequest& isCancellationRequested);
std::pair<bool, std::string> readLine(const CancellationRequest& isCancellationRequested);
std::pair<bool, std::string> readBytes(size_t length,
const OnProgressCallback& onProgressCallback,
const OnChunkCallback& onChunkCallback,
const CancellationRequest& isCancellationRequested);
static int getErrno();
static bool isWaitNeeded();
static void closeSocket(int fd);
// Used as special codes for pipe communication
static const uint64_t kSendRequest;
static const uint64_t kCloseRequest;
static PollResultType poll(bool readyToRead,
int timeoutMs,
int sockfd,
const SelectInterruptPtr& selectInterrupt);
protected:
std::atomic<int> _sockfd;
std::mutex _socketMutex;
private:
PollResultType select(bool readyToRead, int timeoutMs);
static bool readSelectInterruptRequest(const SelectInterruptPtr& selectInterrupt,
PollResultType* pollResult);
private:
static const int kDefaultPollTimeout;
static const int kDefaultPollNoTimeout;
// Buffer for reading from our socket. That buffer is never resized.
std::vector<uint8_t> _readBuffer;
static constexpr size_t kChunkSize = 1 << 15;
std::shared_ptr<SelectInterrupt> _selectInterrupt;
SelectInterruptPtr _selectInterrupt;
};
}
} // namespace ix

View File

@ -1,16 +1,20 @@
/*
* IXSocketAppleSSL.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*
* Adapted from Satori SDK Apple SSL code.
*/
#include "IXSocketAppleSSL.h"
#include "IXSocketConnect.h"
#ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT
#include "IXSocketAppleSSL.h"
#include "IXSocketConnect.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -18,131 +22,16 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#define socketerrno errno
#include <Security/SecureTransport.h>
namespace {
OSStatus read_from_socket(SSLConnectionRef connection, void *data, size_t *len)
{
int fd = (int) (long) connection;
if (fd < 0)
return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
size_t requested_sz = *len;
ssize_t status = read(fd, data, requested_sz);
if (status > 0)
{
*len = (size_t) status;
if (requested_sz > *len)
return errSSLWouldBlock;
else
return noErr;
}
else if (0 == status)
{
*len = 0;
return errSSLClosedGraceful;
}
else
{
*len = 0;
switch (errno) {
case ENOENT:
return errSSLClosedGraceful;
case EAGAIN:
return errSSLWouldBlock;
case ECONNRESET:
return errSSLClosedAbort;
default:
return errSecIO;
}
}
}
OSStatus write_to_socket(SSLConnectionRef connection, const void *data, size_t *len)
{
int fd = (int) (long) connection;
if (fd < 0)
return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
size_t to_write_sz = *len;
ssize_t status = write(fd, data, to_write_sz);
if (status > 0)
{
*len = (size_t) status;
if (to_write_sz > *len)
return errSSLWouldBlock;
else
return noErr;
}
else if (0 == status)
{
*len = 0;
return errSSLClosedGraceful;
}
else
{
*len = 0;
if (EAGAIN == errno)
{
return errSSLWouldBlock;
}
else
{
return errSecIO;
}
}
}
std::string getSSLErrorDescription(OSStatus status)
{
std::string errMsg("Unknown SSL error.");
CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
if (error)
{
CFStringRef message = CFErrorCopyDescription(error);
if (message)
{
char localBuffer[128];
Boolean success;
success = CFStringGetCString(message, localBuffer, 128,
CFStringGetSystemEncoding());
if (success)
{
errMsg = localBuffer;
}
CFRelease(message);
}
CFRelease(error);
}
return errMsg;
}
} // anonymous namespace
namespace ix
{
SocketAppleSSL::SocketAppleSSL(int fd) : Socket(fd),
_sslContext(nullptr)
SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd)
, _sslContext(nullptr)
, _tlsOptions(tlsOptions)
{
;
}
@ -152,6 +41,151 @@ namespace ix
SocketAppleSSL::close();
}
std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status)
{
std::string errMsg("Unknown SSL error.");
CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
if (error)
{
CFStringRef message = CFErrorCopyDescription(error);
if (message)
{
char localBuffer[128];
Boolean success;
success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8);
if (success)
{
errMsg = localBuffer;
}
CFRelease(message);
}
CFRelease(error);
}
return errMsg;
}
OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection, void* data, size_t* len)
{
int fd = (int) (long) connection;
if (fd < 0) return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
size_t requested_sz = *len;
ssize_t status = read(fd, data, requested_sz);
if (status > 0)
{
*len = (size_t) status;
if (requested_sz > *len)
{
return errSSLWouldBlock;
}
else
{
return noErr;
}
}
else if (status == 0)
{
*len = 0;
return errSSLClosedGraceful;
}
else
{
*len = 0;
switch (errno)
{
case ENOENT: return errSSLClosedGraceful;
case EAGAIN: return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN on osx
case EINPROGRESS: return errSSLWouldBlock;
case ECONNRESET: return errSSLClosedAbort;
default: return errSecIO;
}
}
}
OSStatus SocketAppleSSL::writeToSocket(SSLConnectionRef connection,
const void* data,
size_t* len)
{
int fd = (int) (long) connection;
if (fd < 0) return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
size_t to_write_sz = *len;
ssize_t status = write(fd, data, to_write_sz);
if (status > 0)
{
*len = (size_t) status;
if (to_write_sz > *len)
{
return errSSLWouldBlock;
}
else
{
return noErr;
}
}
else if (status == 0)
{
*len = 0;
return errSSLClosedGraceful;
}
else
{
*len = 0;
switch (errno)
{
case ENOENT: return errSSLClosedGraceful;
case EAGAIN: return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN on osx
case EINPROGRESS: return errSSLWouldBlock;
case ECONNRESET: return errSSLClosedAbort;
default: return errSecIO;
}
}
}
bool SocketAppleSSL::accept(std::string& errMsg)
{
errMsg = "TLS not supported yet in server mode with apple ssl backend";
return false;
}
OSStatus SocketAppleSSL::tlsHandShake(std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
OSStatus status;
do
{
status = SSLHandshake(_sslContext);
// Interrupt the handshake
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
return errSSLInternal;
}
} while (status == errSSLWouldBlock || status == errSSLServerAuthCompleted);
return status;
}
// No wait support
bool SocketAppleSSL::connect(const std::string& host,
int port,
@ -167,18 +201,34 @@ namespace ix
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
SSLSetIOFuncs(_sslContext, read_from_socket, write_to_socket);
SSLSetConnection(_sslContext, (SSLConnectionRef) (long) _sockfd);
SSLSetIOFuncs(
_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
do {
status = SSLHandshake(_sslContext);
} while (errSSLWouldBlock == status ||
errSSLServerAuthCompleted == status);
if (!_tlsOptions.disable_hostname_validation)
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
if (_tlsOptions.isPeerVerifyDisabled())
{
Boolean option(1);
SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option);
status = tlsHandShake(errMsg, isCancellationRequested);
if (status == errSSLServerAuthCompleted)
{
// proceed with the handshake
status = tlsHandShake(errMsg, isCancellationRequested);
}
}
else
{
status = tlsHandShake(errMsg, isCancellationRequested);
}
}
if (noErr != status)
if (status != noErr)
{
errMsg = getSSLErrorDescription(status);
close();
@ -202,45 +252,19 @@ namespace ix
}
ssize_t SocketAppleSSL::send(char* buf, size_t nbyte)
{
ssize_t ret = 0;
OSStatus status;
do {
size_t processed = 0;
std::lock_guard<std::mutex> lock(_mutex);
status = SSLWrite(_sslContext, buf, nbyte, &processed);
ret += processed;
buf += processed;
nbyte -= processed;
} while (nbyte > 0 && errSSLWouldBlock == status);
if (ret == 0 && errSSLClosedAbort != status)
ret = -1;
return ret;
}
ssize_t SocketAppleSSL::send(const std::string& buffer)
{
return send((char*)&buffer[0], buffer.size());
}
// No wait support
ssize_t SocketAppleSSL::recv(void* buf, size_t nbyte)
{
OSStatus status = errSSLWouldBlock;
while (errSSLWouldBlock == status)
while (status == errSSLWouldBlock)
{
size_t processed = 0;
std::lock_guard<std::mutex> lock(_mutex);
status = SSLRead(_sslContext, buf, nbyte, &processed);
status = SSLWrite(_sslContext, buf, nbyte, &processed);
if (processed > 0)
return (ssize_t) processed;
if (processed > 0) return (ssize_t) processed;
// The connection was reset, inform the caller that this
// Socket should close
if (status == errSSLClosedGraceful ||
status == errSSLClosedNoNotify ||
if (status == errSSLClosedGraceful || status == errSSLClosedNoNotify ||
status == errSSLClosedAbort)
{
errno = ECONNRESET;
@ -256,4 +280,36 @@ namespace ix
return -1;
}
}
// No wait support
ssize_t SocketAppleSSL::recv(void* buf, size_t nbyte)
{
OSStatus status = errSSLWouldBlock;
while (status == errSSLWouldBlock)
{
size_t processed = 0;
std::lock_guard<std::mutex> lock(_mutex);
status = SSLRead(_sslContext, buf, nbyte, &processed);
if (processed > 0) return (ssize_t) processed;
// The connection was reset, inform the caller that this
// Socket should close
if (status == errSSLClosedGraceful || status == errSSLClosedNoNotify ||
status == errSSLClosedAbort)
{
errno = ECONNRESET;
return -1;
}
if (status == errSSLWouldBlock)
{
errno = EWOULDBLOCK;
return -1;
}
}
return -1;
}
} // namespace ix
#endif // IXWEBSOCKET_USE_SECURE_TRANSPORT

View File

@ -1,17 +1,17 @@
/*
* IXSocketAppleSSL.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT
#pragma once
#include "IXSocket.h"
#include "IXCancellationRequest.h"
#include <Security/Security.h>
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include <Security/SecureTransport.h>
#include <Security/Security.h>
#include <mutex>
namespace ix
@ -19,9 +19,11 @@ namespace ix
class SocketAppleSSL final : public Socket
{
public:
SocketAppleSSL(int fd = -1);
SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd = -1);
~SocketAppleSSL();
virtual bool accept(std::string& errMsg) final;
virtual bool connect(const std::string& host,
int port,
std::string& errMsg,
@ -29,12 +31,22 @@ namespace ix
virtual void close() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t send(const std::string& buffer) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:
static std::string getSSLErrorDescription(OSStatus status);
static OSStatus writeToSocket(SSLConnectionRef connection, const void* data, size_t* len);
static OSStatus readFromSocket(SSLConnectionRef connection, void* data, size_t* len);
OSStatus tlsHandShake(std::string& errMsg,
const CancellationRequest& isCancellationRequested);
SSLContextRef _sslContext;
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
SocketTLSOptions _tlsOptions;
};
}
} // namespace ix
#endif // IXWEBSOCKET_USE_SECURE_TRANSPORT

View File

@ -5,36 +5,38 @@
*/
#include "IXSocketConnect.h"
#include "IXDNSLookup.h"
#include "IXNetSystem.h"
#include "IXSelectInterrupt.h"
#include "IXSocket.h"
#include <string.h>
#include "IXUniquePtr.h"
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
// Android needs extra headers for TCP_NODELAY and IPPROTO_TCP
#ifdef ANDROID
# include <linux/in.h>
# include <linux/tcp.h>
#include <linux/in.h>
#include <linux/tcp.h>
#endif
#include <ixwebsocket/IXSelectInterruptFactory.h>
namespace ix
{
//
// This function can be cancelled every 50 ms
// This is important so that we don't block the main UI thread when shutting down a connection which is
// already trying to reconnect, and can be blocked waiting for ::connect to respond.
// This is important so that we don't block the main UI thread when shutting down a
// connection which is already trying to reconnect, and can be blocked waiting for
// ::connect to respond.
//
int SocketConnect::connectToAddress(const struct addrinfo *address,
int SocketConnect::connectToAddress(const struct addrinfo* address,
std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
errMsg = "no error";
int fd = socket(address->ai_family,
address->ai_socktype,
address->ai_protocol);
socket_t fd = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
if (fd < 0)
{
errMsg = "Cannot create a socket";
@ -63,61 +65,32 @@ namespace ix
return -1;
}
// On Linux the timeout needs to be re-initialized everytime
// http://man7.org/linux/man-pages/man2/select.2.html
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10 * 1000; // 10ms timeout
int timeoutMs = 10;
bool readyToRead = false;
SelectInterruptPtr selectInterrupt = ix::createSelectInterrupt();
PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
fd_set wfds;
fd_set efds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
FD_ZERO(&efds);
FD_SET(fd, &efds);
// Use select to check the status of the new connection
res = select(fd + 1, nullptr, &wfds, &efds, &timeout);
if (res < 0 && (Socket::getErrno() == EBADF || Socket::getErrno() == EINVAL))
if (pollResult == PollResultType::Timeout)
{
continue;
}
else if (pollResult == PollResultType::Error)
{
Socket::closeSocket(fd);
errMsg = std::string("Connect error, select error: ") + strerror(Socket::getErrno());
errMsg = std::string("Connect error: ") + strerror(Socket::getErrno());
return -1;
}
// Nothing was written to the socket, wait again.
if (!FD_ISSET(fd, &wfds)) continue;
// Something was written to the socket. Check for errors.
int optval = -1;
socklen_t optlen = sizeof(optval);
#ifdef _WIN32
// On connect error, in async mode, windows will write to the exceptions fds
if (FD_ISSET(fd, &efds))
#else
// getsockopt() puts the errno value for connect into optval so 0
// means no-error.
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1 ||
optval != 0)
#endif
else if (pollResult == PollResultType::ReadyForWrite)
{
Socket::closeSocket(fd);
errMsg = strerror(optval);
return -1;
return fd;
}
else
{
// Success !
return fd;
Socket::closeSocket(fd);
errMsg = std::string("Connect error: ") + strerror(Socket::getErrno());
return -1;
}
}
Socket::closeSocket(fd);
errMsg = "connect timed out after 60 seconds";
return -1;
}
int SocketConnect::connect(const std::string& hostname,
@ -128,8 +101,8 @@ namespace ix
//
// First do DNS resolution
//
DNSLookup dnsLookup(hostname, port);
struct addrinfo *res = dnsLookup.resolve(errMsg, isCancellationRequested);
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
auto res = dnsLookup->resolve(errMsg, isCancellationRequested);
if (res == nullptr)
{
return -1;
@ -138,8 +111,8 @@ namespace ix
int sockfd = -1;
// iterate through the records to find a working peer
struct addrinfo *address;
for (address = res; address != nullptr; address = address->ai_next)
struct addrinfo* address;
for (address = res.get(); address != nullptr; address = address->ai_next)
{
//
// Second try to connect to the remote host
@ -151,7 +124,6 @@ namespace ix
}
}
freeaddrinfo(res);
return sockfd;
}
@ -173,8 +145,7 @@ namespace ix
// 3. (apple) prevent SIGPIPE from being emitted when the remote end disconnect
#ifdef SO_NOSIGPIPE
int value = 1;
setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
(void *)&value, sizeof(value));
setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*) &value, sizeof(value));
#endif
}
}
} // namespace ix

View File

@ -7,14 +7,14 @@
#pragma once
#include "IXCancellationRequest.h"
#include <string>
struct addrinfo;
namespace ix
{
class SocketConnect {
class SocketConnect
{
public:
static int connect(const std::string& hostname,
int port,
@ -24,9 +24,8 @@ namespace ix
static void configure(int sockfd);
private:
static int connectToAddress(const struct addrinfo *address,
static int connectToAddress(const struct addrinfo* address,
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
};
}
} // namespace ix

View File

@ -6,44 +6,48 @@
#include "IXSocketFactory.h"
#include "IXUniquePtr.h"
#ifdef IXWEBSOCKET_USE_TLS
# ifdef __APPLE__
# include <ixwebsocket/IXSocketAppleSSL.h>
# elif defined(_WIN32)
# include <ixwebsocket/IXSocketSChannel.h>
# else
# include <ixwebsocket/IXSocketOpenSSL.h>
# endif
#ifdef IXWEBSOCKET_USE_MBED_TLS
#include "IXSocketMbedTLS.h"
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
#include "IXSocketOpenSSL.h"
#elif __APPLE__
#include "IXSocketAppleSSL.h"
#endif
#else
#include <ixwebsocket/IXSocket.h>
#include "IXSocket.h"
#endif
namespace ix
{
std::shared_ptr<Socket> createSocket(bool tls,
std::string& errorMsg)
std::unique_ptr<Socket> createSocket(bool tls,
int fd,
std::string& errorMsg,
const SocketTLSOptions& tlsOptions)
{
(void) tlsOptions;
errorMsg.clear();
std::shared_ptr<Socket> socket;
std::unique_ptr<Socket> socket;
if (!tls)
{
socket = std::make_shared<Socket>();
socket = ix::make_unique<Socket>(fd);
}
else
{
#ifdef IXWEBSOCKET_USE_TLS
# ifdef __APPLE__
socket = std::make_shared<SocketAppleSSL>();
# elif defined(_WIN32)
socket = std::make_shared<SocketSChannel>();
# else
socket = std::make_shared<SocketOpenSSL>();
# endif
#if defined(IXWEBSOCKET_USE_MBED_TLS)
socket = ix::make_unique<SocketMbedTLS>(tlsOptions, fd);
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
socket = ix::make_unique<SocketOpenSSL>(tlsOptions, fd);
#elif defined(__APPLE__)
socket = ix::make_unique<SocketAppleSSL>(tlsOptions, fd);
#endif
#else
errorMsg = "TLS support is not enabled on this platform.";
return nullptr;
@ -57,18 +61,4 @@ namespace ix
return socket;
}
std::shared_ptr<Socket> createSocket(int fd,
std::string& errorMsg)
{
errorMsg.clear();
std::shared_ptr<Socket> socket = std::make_shared<Socket>(fd);
if (!socket->init(errorMsg))
{
socket.reset();
}
return socket;
}
}
} // namespace ix

View File

@ -7,15 +7,15 @@
#pragma once
#include "IXSocketTLSOptions.h"
#include <memory>
#include <string>
namespace ix
{
class Socket;
std::shared_ptr<Socket> createSocket(bool tls,
std::string& errorMsg);
std::shared_ptr<Socket> createSocket(int fd,
std::string& errorMsg);
}
std::unique_ptr<Socket> createSocket(bool tls,
int fd,
std::string& errorMsg,
const SocketTLSOptions& tlsOptions);
} // namespace ix

View File

@ -0,0 +1,377 @@
/*
* IXSocketMbedTLS.cpp
* Author: Benjamin Sergeant, Max Weisel
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
*
* Some code taken from
* https://github.com/rottor12/WsClientLib/blob/master/lib/src/WsClientLib.cpp
* and mini_client.c example from mbedtls
*/
#ifdef IXWEBSOCKET_USE_MBED_TLS
#include "IXSocketMbedTLS.h"
#include "IXNetSystem.h"
#include "IXSocket.h"
#include "IXSocketConnect.h"
#include <cstdint>
#include <string.h>
#ifdef _WIN32
// For manipulating the certificate store
#include <wincrypt.h>
#endif
namespace ix
{
SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd)
, _tlsOptions(tlsOptions)
{
initMBedTLS();
}
SocketMbedTLS::~SocketMbedTLS()
{
SocketMbedTLS::close();
}
void SocketMbedTLS::initMBedTLS()
{
std::lock_guard<std::mutex> lock(_mutex);
mbedtls_ssl_init(&_ssl);
mbedtls_ssl_config_init(&_conf);
mbedtls_ctr_drbg_init(&_ctr_drbg);
mbedtls_entropy_init(&_entropy);
mbedtls_x509_crt_init(&_cacert);
mbedtls_x509_crt_init(&_cert);
mbedtls_pk_init(&_pkey);
// Initialize the PSA Crypto API if required by the version of Mbed TLS (3.6.0).
// This allows the X.509/TLS libraries to use PSA for crypto operations.
// See: https://github.com/Mbed-TLS/mbedtls/blob/development/docs/use-psa-crypto.md
if (MBEDTLS_VERSION_MAJOR >= 3 && MBEDTLS_VERSION_MINOR >= 6 && MBEDTLS_VERSION_PATCH >= 0)
{
psa_crypto_init();
}
}
bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg)
{
#ifdef _WIN32
DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG |
CERT_SYSTEM_STORE_CURRENT_USER;
HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root");
if (!systemStore)
{
errorMsg = "CertOpenStore failed with ";
errorMsg += std::to_string(GetLastError());
return false;
}
PCCERT_CONTEXT certificateIterator = NULL;
int certificateCount = 0;
while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator))
{
if (certificateIterator->dwCertEncodingType & X509_ASN_ENCODING)
{
int ret = mbedtls_x509_crt_parse(&_cacert,
certificateIterator->pbCertEncoded,
certificateIterator->cbCertEncoded);
if (ret == 0)
{
++certificateCount;
}
}
}
CertFreeCertificateContext(certificateIterator);
CertCloseStore(systemStore, 0);
if (certificateCount == 0)
{
errorMsg = "No certificates found";
return false;
}
return true;
#else
// On macOS we can query the system cert location from the keychain
// On Linux we could try to fetch some local files based on the distribution
// On Android we could use JNI to get to the system certs
return false;
#endif
}
bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg)
{
initMBedTLS();
std::lock_guard<std::mutex> lock(_mutex);
const char* pers = "IXSocketMbedTLS";
if (mbedtls_ctr_drbg_seed(&_ctr_drbg,
mbedtls_entropy_func,
&_entropy,
(const unsigned char*) pers,
strlen(pers)) != 0)
{
errMsg = "Setting entropy seed failed";
return false;
}
if (mbedtls_ssl_config_defaults(&_conf,
(isClient) ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT) != 0)
{
errMsg = "Setting config default failed";
return false;
}
mbedtls_ssl_conf_rng(&_conf, mbedtls_ctr_drbg_random, &_ctr_drbg);
if (_tlsOptions.hasCertAndKey())
{
if (mbedtls_x509_crt_parse_file(&_cert, _tlsOptions.certFile.c_str()) < 0)
{
errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'";
return false;
}
#ifdef IXWEBSOCKET_USE_MBED_TLS_MIN_VERSION_3
if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "", mbedtls_ctr_drbg_random, &_ctr_drbg) < 0)
#else
if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < 0)
#endif
{
errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'";
return false;
}
if (mbedtls_ssl_conf_own_cert(&_conf, &_cert, &_pkey) < 0)
{
errMsg = "Problem configuring cert '" + _tlsOptions.certFile + "'";
return false;
}
}
if (_tlsOptions.isPeerVerifyDisabled())
{
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_NONE);
}
else
{
// FIXME: should we call mbedtls_ssl_conf_verify ?
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
if (_tlsOptions.isUsingSystemDefaults())
{
if (!loadSystemCertificates(errMsg))
{
return false;
}
}
else
{
if (_tlsOptions.isUsingInMemoryCAs())
{
const char* buffer = _tlsOptions.caFile.c_str();
size_t bufferSize =
_tlsOptions.caFile.size() + 1; // Needs to include null terminating
// character otherwise mbedtls will fail.
if (mbedtls_x509_crt_parse(
&_cacert, (const unsigned char*) buffer, bufferSize) < 0)
{
errMsg = "Cannot parse CA from memory.";
return false;
}
}
else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0)
{
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
return false;
}
}
mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
}
if (mbedtls_ssl_setup(&_ssl, &_conf) != 0)
{
errMsg = "SSL setup failed";
return false;
}
if (!_tlsOptions.disable_hostname_validation)
{
if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
{
errMsg = "SNI setup failed";
return false;
}
}
return true;
}
bool SocketMbedTLS::accept(std::string& errMsg)
{
bool isClient = false;
bool initialized = init(std::string(), isClient, errMsg);
if (!initialized)
{
close();
return false;
}
mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
int res;
do
{
std::lock_guard<std::mutex> lock(_mutex);
res = mbedtls_ssl_handshake(&_ssl);
} while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
if (res != 0)
{
char buf[256];
mbedtls_strerror(res, buf, sizeof(buf));
errMsg = "error in handshake : ";
errMsg += buf;
if (res == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED)
{
char verifyBuf[512];
uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl);
mbedtls_x509_crt_verify_info(verifyBuf, sizeof(verifyBuf), " ! ", flags);
errMsg += " : ";
errMsg += verifyBuf;
}
close();
return false;
}
return true;
}
bool SocketMbedTLS::connect(const std::string& host,
int port,
std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
{
std::lock_guard<std::mutex> lock(_mutex);
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1) return false;
}
bool isClient = true;
bool initialized = init(host, isClient, errMsg);
if (!initialized)
{
close();
return false;
}
mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
int res;
do
{
{
std::lock_guard<std::mutex> lock(_mutex);
res = mbedtls_ssl_handshake(&_ssl);
}
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
close();
return false;
}
} while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
if (res != 0)
{
char buf[256];
mbedtls_strerror(res, buf, sizeof(buf));
errMsg = "error in handshake : ";
errMsg += buf;
close();
return false;
}
return true;
}
void SocketMbedTLS::close()
{
std::lock_guard<std::mutex> lock(_mutex);
mbedtls_ssl_free(&_ssl);
mbedtls_ssl_config_free(&_conf);
mbedtls_ctr_drbg_free(&_ctr_drbg);
mbedtls_entropy_free(&_entropy);
mbedtls_x509_crt_free(&_cacert);
mbedtls_x509_crt_free(&_cert);
Socket::close();
}
ssize_t SocketMbedTLS::send(char* buf, size_t nbyte)
{
std::lock_guard<std::mutex> lock(_mutex);
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
if (res > 0)
{
return res;
}
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
}
else
{
return -1;
}
}
ssize_t SocketMbedTLS::recv(void* buf, size_t nbyte)
{
while (true)
{
std::lock_guard<std::mutex> lock(_mutex);
ssize_t res = mbedtls_ssl_read(&_ssl, (unsigned char*) buf, (int) nbyte);
if (res > 0)
{
return res;
}
if (res == 0)
{
errno = ECONNRESET;
}
if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{
errno = EWOULDBLOCK;
}
return -1;
}
}
} // namespace ix
#endif // IXWEBSOCKET_USE_MBED_TLS

View File

@ -0,0 +1,60 @@
/*
* IXSocketMbedTLS.h
* Author: Benjamin Sergeant
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_MBED_TLS
#pragma once
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/debug.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/platform.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#include <mutex>
namespace ix
{
class SocketMbedTLS final : public Socket
{
public:
SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd = -1);
~SocketMbedTLS();
virtual bool accept(std::string& errMsg) final;
virtual bool connect(const std::string& host,
int port,
std::string& errMsg,
const CancellationRequest& isCancellationRequested) final;
virtual void close() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:
mbedtls_ssl_context _ssl;
mbedtls_ssl_config _conf;
mbedtls_entropy_context _entropy;
mbedtls_ctr_drbg_context _ctr_drbg;
mbedtls_x509_crt _cacert;
mbedtls_x509_crt _cert;
mbedtls_pk_context _pkey;
std::mutex _mutex;
SocketTLSOptions _tlsOptions;
bool init(const std::string& host, bool isClient, std::string& errMsg);
void initMBedTLS();
bool loadSystemCertificates(std::string& errMsg);
};
} // namespace ix
#endif // IXWEBSOCKET_USE_MBED_TLS

View File

@ -1,30 +1,105 @@
/*
* IXSocketOpenSSL.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
* Author: Benjamin Sergeant, Matt DeBoer, Max Weisel
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*
* Adapted from Satori SDK OpenSSL code.
*/
#ifdef IXWEBSOCKET_USE_OPEN_SSL
#include "IXSocketOpenSSL.h"
#include "IXSocketConnect.h"
#include "IXUniquePtr.h"
#include <cassert>
#include <openssl/x509v3.h>
#include <fnmatch.h>
#include <errno.h>
#include <vector>
#ifdef _WIN32
#include <shlwapi.h>
#else
#include <fnmatch.h>
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#include <openssl/x509v3.h>
#endif
#define socketerrno errno
#ifdef _WIN32
// For manipulating the certificate store
#include <windows.h>
#include <wincrypt.h>
#endif
#ifdef _WIN32
namespace
{
bool loadWindowsSystemCertificates(SSL_CTX* ssl, std::string& errorMsg)
{
DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG |
CERT_SYSTEM_STORE_CURRENT_USER;
HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root");
if (!systemStore)
{
errorMsg = "CertOpenStore failed with ";
errorMsg += std::to_string(GetLastError());
return false;
}
PCCERT_CONTEXT certificateIterator = NULL;
X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl);
int certificateCount = 0;
while ((certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator)))
{
X509* x509 = d2i_X509(NULL,
(const unsigned char**) &certificateIterator->pbCertEncoded,
certificateIterator->cbCertEncoded);
if (x509)
{
if (X509_STORE_add_cert(opensslStore, x509) == 1)
{
++certificateCount;
}
X509_free(x509);
}
}
CertFreeCertificateContext(certificateIterator);
CertCloseStore(systemStore, 0);
if (certificateCount == 0)
{
errorMsg = "No certificates found";
return false;
}
return true;
}
} // namespace
#endif
namespace ix
{
const std::string kDefaultCiphers =
"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA "
"ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 "
"ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA "
"ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 "
"DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-SHA "
"DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256 AES128-SHA";
std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
std::once_flag SocketOpenSSL::_openSSLInitFlag;
std::vector<std::unique_ptr<std::mutex>> openSSLMutexes;
SocketOpenSSL::SocketOpenSSL(int fd) : Socket(fd),
_ssl_connection(nullptr),
_ssl_context(nullptr)
SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd)
, _ssl_connection(nullptr)
, _ssl_context(nullptr)
, _tlsOptions(tlsOptions)
{
std::call_once(_openSSLInitFlag, &SocketOpenSSL::openSSLInitialize, this);
}
@ -40,6 +115,16 @@ namespace ix
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return;
#else
(void) OPENSSL_config(nullptr);
if (CRYPTO_get_locking_callback() == nullptr)
{
openSSLMutexes.clear();
for (int i = 0; i < CRYPTO_num_locks(); ++i)
{
openSSLMutexes.push_back(ix::make_unique<std::mutex>());
}
CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback);
}
#endif
(void) OpenSSL_add_ssl_algorithms();
@ -48,6 +133,21 @@ namespace ix
_openSSLInitializationSuccessful = true;
}
void SocketOpenSSL::openSSLLockingCallback(int mode,
int type,
const char* /*file*/,
int /*line*/)
{
if (mode & CRYPTO_LOCK)
{
openSSLMutexes[type]->lock();
}
else
{
openSSLMutexes[type]->unlock();
}
}
std::string SocketOpenSSL::getSSLError(int ret)
{
unsigned long e;
@ -114,32 +214,108 @@ namespace ix
SSL_CTX* ctx = SSL_CTX_new(_ssl_method);
if (ctx)
{
// To skip verification, pass in SSL_VERIFY_NONE
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER,
[](int preverify, X509_STORE_CTX*) -> int
{
return preverify;
});
SSL_CTX_set_mode(ctx,
SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_verify_depth(ctx, 4);
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
int options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE;
#ifdef SSL_OP_NO_TLSv1_3
// (partially?) work around hang in openssl 1.1.1b, by disabling TLS V1.3
// https://github.com/openssl/openssl/issues/7967
options |= SSL_OP_NO_TLSv1_3;
#endif
SSL_CTX_set_options(ctx, options);
}
return ctx;
}
bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots)
{
// Create certificate store
X509_STORE* certificate_store = SSL_CTX_get_cert_store(_ssl_context);
if (certificate_store == nullptr) return false;
// Configure to allow intermediate certs
X509_STORE_set_flags(certificate_store,
X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN);
// Create a new buffer and populate it with the roots
BIO* buffer = BIO_new_mem_buf((void*) roots.c_str(), static_cast<int>(roots.length()));
if (buffer == nullptr) return false;
// Read each root in the buffer and add to the certificate store
bool success = true;
size_t number_of_roots = 0;
while (true)
{
// Read the next root in the buffer
X509* root = PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void*) "");
if (root == nullptr)
{
// No more certs left in the buffer, we're done.
ERR_clear_error();
break;
}
// Try adding the root to the certificate store
ERR_clear_error();
if (!X509_STORE_add_cert(certificate_store, root))
{
// Failed to add. If the error is unrelated to the x509 lib or the cert already
// exists, we're safe to continue.
unsigned long error = ERR_get_error();
if (ERR_GET_LIB(error) != ERR_LIB_X509 ||
ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE)
{
// Failed. Clean up and bail.
success = false;
X509_free(root);
break;
}
}
// Clean up and loop
X509_free(root);
number_of_roots++;
}
// Clean up buffer
BIO_free(buffer);
// Make sure we loaded at least one certificate.
if (number_of_roots == 0) success = false;
return success;
}
/**
* Check whether a hostname matches a pattern
*/
bool SocketOpenSSL::checkHost(const std::string& host, const char *pattern)
bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
return true;
#else
#ifdef _WIN32
return PathMatchSpecA(host.c_str(), pattern);
#else
return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH;
#endif
#endif
}
bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl,
bool SocketOpenSSL::openSSLCheckServerCert(SSL* ssl,
#if OPENSSL_VERSION_NUMBER < 0x10100000L
const std::string& hostname,
#else
const std::string& /* hostname */,
#endif
std::string& errMsg)
{
X509 *server_cert = SSL_get_peer_certificate(ssl);
X509* server_cert = SSL_get_peer_certificate(ssl);
if (server_cert == nullptr)
{
errMsg = "OpenSSL failed - peer didn't present a X509 certificate.";
@ -149,18 +325,17 @@ namespace ix
#if OPENSSL_VERSION_NUMBER < 0x10100000L
// Check server name
bool hostname_verifies_ok = false;
STACK_OF(GENERAL_NAME) *san_names =
(STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i((X509 *)server_cert,
NID_subject_alt_name, NULL, NULL);
STACK_OF(GENERAL_NAME)* san_names = (STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i(
(X509*) server_cert, NID_subject_alt_name, NULL, NULL);
if (san_names)
{
for (int i=0; i<sk_GENERAL_NAME_num(san_names); i++)
for (int i = 0; i < sk_GENERAL_NAME_num(san_names); i++)
{
const GENERAL_NAME *sk_name = sk_GENERAL_NAME_value(san_names, i);
const GENERAL_NAME* sk_name = sk_GENERAL_NAME_value(san_names, i);
if (sk_name->type == GEN_DNS)
{
char *name = (char *)ASN1_STRING_data(sk_name->d.dNSName);
if ((size_t)ASN1_STRING_length(sk_name->d.dNSName) == strlen(name) &&
char* name = (char*) ASN1_STRING_data(sk_name->d.dNSName);
if ((size_t) ASN1_STRING_length(sk_name->d.dNSName) == strlen(name) &&
checkHost(hostname, name))
{
hostname_verifies_ok = true;
@ -173,20 +348,20 @@ namespace ix
if (!hostname_verifies_ok)
{
int cn_pos = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *)server_cert),
NID_commonName, -1);
if (cn_pos)
int cn_pos = X509_NAME_get_index_by_NID(
X509_get_subject_name((X509*) server_cert), NID_commonName, -1);
if (cn_pos >= 0)
{
X509_NAME_ENTRY *cn_entry = X509_NAME_get_entry(
X509_get_subject_name((X509 *)server_cert), cn_pos);
X509_NAME_ENTRY* cn_entry =
X509_NAME_get_entry(X509_get_subject_name((X509*) server_cert), cn_pos);
if (cn_entry)
if (cn_entry != nullptr)
{
ASN1_STRING *cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
char *cn = (char *)ASN1_STRING_data(cn_asn1);
ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
char* cn = (char*) ASN1_STRING_data(cn_asn1);
if ((size_t)ASN1_STRING_length(cn_asn1) == strlen(cn) &&
checkHost(hostname, cn))
if ((size_t) ASN1_STRING_length(cn_asn1) == strlen(cn) &&
checkHost(hostname, cn))
{
hostname_verifies_ok = true;
}
@ -205,7 +380,9 @@ namespace ix
return true;
}
bool SocketOpenSSL::openSSLHandshake(const std::string& host, std::string& errMsg)
bool SocketOpenSSL::openSSLClientHandshake(const std::string& host,
std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
while (true)
{
@ -214,10 +391,21 @@ namespace ix
return false;
}
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
return false;
}
ERR_clear_error();
int connect_result = SSL_connect(_ssl_connection);
if (connect_result == 1)
{
if (_tlsOptions.disable_hostname_validation)
{
return true;
}
return openSSLCheckServerCert(_ssl_connection, host, errMsg);
}
int reason = SSL_get_error(_ssl_connection, connect_result);
@ -240,7 +428,301 @@ namespace ix
}
}
// No wait support
bool SocketOpenSSL::openSSLServerHandshake(std::string& errMsg)
{
while (true)
{
if (_ssl_connection == nullptr || _ssl_context == nullptr)
{
return false;
}
ERR_clear_error();
int accept_result = SSL_accept(_ssl_connection);
if (accept_result == 1)
{
return true;
}
int reason = SSL_get_error(_ssl_connection, accept_result);
bool rc = false;
if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
rc = true;
}
else
{
errMsg = getSSLError(accept_result);
rc = false;
}
if (!rc)
{
return false;
}
}
}
bool SocketOpenSSL::handleTLSOptions(std::string& errMsg)
{
ERR_clear_error();
if (_tlsOptions.hasCertAndKey())
{
if (SSL_CTX_use_certificate_chain_file(_ssl_context, _tlsOptions.certFile.c_str()) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" +
_tlsOptions.certFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
else if (SSL_CTX_use_PrivateKey_file(
_ssl_context, _tlsOptions.keyFile.c_str(), SSL_FILETYPE_PEM) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" + _tlsOptions.keyFile +
"\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
else if (!SSL_CTX_check_private_key(_ssl_context))
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - cert/key mismatch(\"" + _tlsOptions.certFile + ", " +
_tlsOptions.keyFile + "\")";
errMsg += ERR_error_string(sslErr, nullptr);
}
}
ERR_clear_error();
if (!_tlsOptions.isPeerVerifyDisabled())
{
if (_tlsOptions.isUsingSystemDefaults())
{
#ifdef _WIN32
if (!loadWindowsSystemCertificates(_ssl_context, errMsg))
{
return false;
}
#else
if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
#endif
}
else
{
if (_tlsOptions.isUsingInMemoryCAs())
{
// Load from memory
openSSLAddCARootsFromString(_tlsOptions.caFile);
}
else
{
if (SSL_CTX_load_verify_locations(
_ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
_tlsOptions.caFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
}
}
SSL_CTX_set_verify(_ssl_context,
SSL_VERIFY_PEER,
[](int preverify, X509_STORE_CTX*) -> int { return preverify; });
SSL_CTX_set_verify_depth(_ssl_context, 4);
}
else
{
SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr);
}
if (_tlsOptions.isUsingDefaultCiphers())
{
if (SSL_CTX_set_cipher_list(_ssl_context, kDefaultCiphers.c_str()) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + kDefaultCiphers +
"\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
}
else if (SSL_CTX_set_cipher_list(_ssl_context, _tlsOptions.ciphers.c_str()) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + _tlsOptions.ciphers +
"\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
return true;
}
bool SocketOpenSSL::accept(std::string& errMsg)
{
bool handshakeSuccessful = false;
{
std::lock_guard<std::mutex> lock(_mutex);
if (!_openSSLInitializationSuccessful)
{
errMsg = "OPENSSL_init_ssl failure";
return false;
}
if (_sockfd == -1)
{
return false;
}
{
const SSL_METHOD* method = SSLv23_server_method();
if (method == nullptr)
{
errMsg = "SSLv23_server_method failure";
_ssl_context = nullptr;
}
else
{
_ssl_method = method;
_ssl_context = SSL_CTX_new(_ssl_method);
if (_ssl_context)
{
SSL_CTX_set_mode(_ssl_context, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_mode(_ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_options(_ssl_context,
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
}
}
}
if (_ssl_context == nullptr)
{
return false;
}
ERR_clear_error();
if (_tlsOptions.hasCertAndKey())
{
if (SSL_CTX_use_certificate_chain_file(_ssl_context,
_tlsOptions.certFile.c_str()) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" +
_tlsOptions.certFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
else if (SSL_CTX_use_PrivateKey_file(
_ssl_context, _tlsOptions.keyFile.c_str(), SSL_FILETYPE_PEM) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" +
_tlsOptions.keyFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
}
ERR_clear_error();
if (!_tlsOptions.isPeerVerifyDisabled())
{
if (_tlsOptions.isUsingSystemDefaults())
{
if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
}
else
{
if (_tlsOptions.isUsingInMemoryCAs())
{
// Load from memory
openSSLAddCARootsFromString(_tlsOptions.caFile);
}
else
{
const char* root_ca_file = _tlsOptions.caFile.c_str();
STACK_OF(X509_NAME) * rootCAs;
rootCAs = SSL_load_client_CA_file(root_ca_file);
if (rootCAs == NULL)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_load_client_CA_file('" +
_tlsOptions.caFile + "') failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
else
{
SSL_CTX_set_client_CA_list(_ssl_context, rootCAs);
if (SSL_CTX_load_verify_locations(
_ssl_context, root_ca_file, nullptr) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
_tlsOptions.caFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
}
}
}
SSL_CTX_set_verify(
_ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
SSL_CTX_set_verify_depth(_ssl_context, 4);
}
else
{
SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr);
}
if (_tlsOptions.isUsingDefaultCiphers())
{
if (SSL_CTX_set_cipher_list(_ssl_context, kDefaultCiphers.c_str()) != 1)
{
return false;
}
}
else if (SSL_CTX_set_cipher_list(_ssl_context, _tlsOptions.ciphers.c_str()) != 1)
{
return false;
}
_ssl_connection = SSL_new(_ssl_context);
if (_ssl_connection == nullptr)
{
errMsg = "OpenSSL failed to connect";
SSL_CTX_free(_ssl_context);
_ssl_context = nullptr;
return false;
}
SSL_set_ecdh_auto(_ssl_connection, 1);
SSL_set_fd(_ssl_connection, _sockfd);
handshakeSuccessful = openSSLServerHandshake(errMsg);
}
if (!handshakeSuccessful)
{
close();
return false;
}
return true;
}
bool SocketOpenSSL::connect(const std::string& host,
int port,
std::string& errMsg,
@ -265,13 +747,9 @@ namespace ix
return false;
}
ERR_clear_error();
int cert_load_result = SSL_CTX_set_default_verify_paths(_ssl_context);
if (cert_load_result == 0)
if (!handleTLSOptions(errMsg))
{
unsigned long ssl_err = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: ";
errMsg += ERR_error_string(ssl_err, nullptr);
return false;
}
_ssl_connection = SSL_new(_ssl_context);
@ -290,13 +768,15 @@ namespace ix
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
// Support for server name verification
// (The docs say that this should work from 1.0.2, and is the default from
// 1.1.0, but it does not. To be on the safe side, the manual test below is
// enabled for all versions prior to 1.1.0.)
X509_VERIFY_PARAM *param = SSL_get0_param(_ssl_connection);
X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0);
// 1.1.0, but it does not. To be on the safe side, the manual test
// below is enabled for all versions prior to 1.1.0.)
if (!_tlsOptions.disable_hostname_validation)
{
X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection);
X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size());
}
#endif
handshakeSuccessful = openSSLHandshake(host, errMsg);
handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested);
}
if (!handshakeSuccessful)
@ -328,40 +808,32 @@ namespace ix
ssize_t SocketOpenSSL::send(char* buf, size_t nbyte)
{
ssize_t sent = 0;
std::lock_guard<std::mutex> lock(_mutex);
while (nbyte > 0)
if (_ssl_connection == nullptr || _ssl_context == nullptr)
{
std::lock_guard<std::mutex> lock(_mutex);
if (_ssl_connection == nullptr || _ssl_context == nullptr)
{
return 0;
}
ERR_clear_error();
ssize_t write_result = SSL_write(_ssl_connection, buf + sent, (int) nbyte);
int reason = SSL_get_error(_ssl_connection, (int) write_result);
if (reason == SSL_ERROR_NONE) {
nbyte -= write_result;
sent += write_result;
} else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) {
errno = EWOULDBLOCK;
return -1;
} else {
return -1;
}
return 0;
}
ERR_clear_error();
ssize_t write_result = SSL_write(_ssl_connection, buf, (int) nbyte);
int reason = SSL_get_error(_ssl_connection, (int) write_result);
if (reason == SSL_ERROR_NONE)
{
return write_result;
}
else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
}
else
{
return -1;
}
return sent;
}
ssize_t SocketOpenSSL::send(const std::string& buffer)
{
return send((char*)&buffer[0], buffer.size());
}
// No wait support
ssize_t SocketOpenSSL::recv(void* buf, size_t nbyte)
{
while (true)
@ -391,4 +863,6 @@ namespace ix
}
}
}
} // namespace ix
#endif // IXWEBSOCKET_USE_OPEN_SSL

View File

@ -1,30 +1,32 @@
/*
* IXSocketOpenSSL.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
* Author: Benjamin Sergeant, Matt DeBoer
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_OPEN_SSL
#pragma once
#include "IXSocket.h"
#include "IXCancellationRequest.h"
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include <mutex>
#include <openssl/bio.h>
#include <openssl/hmac.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/ssl.h>
#include <mutex>
namespace ix
{
class SocketOpenSSL final : public Socket
{
public:
SocketOpenSSL(int fd = -1);
SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd = -1);
~SocketOpenSSL();
virtual bool accept(std::string& errMsg) final;
virtual bool connect(const std::string& host,
int port,
std::string& errMsg,
@ -32,26 +34,35 @@ namespace ix
virtual void close() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t send(const std::string& buffer) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:
void openSSLInitialize();
std::string getSSLError(int ret);
SSL_CTX* openSSLCreateContext(std::string& errMsg);
bool openSSLHandshake(const std::string& hostname, std::string& errMsg);
bool openSSLCheckServerCert(SSL *ssl,
const std::string& hostname,
std::string& errMsg);
bool checkHost(const std::string& host, const char *pattern);
bool openSSLAddCARootsFromString(const std::string roots);
bool openSSLClientHandshake(const std::string& hostname,
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
bool openSSLCheckServerCert(SSL* ssl, const std::string& hostname, std::string& errMsg);
bool checkHost(const std::string& host, const char* pattern);
bool handleTLSOptions(std::string& errMsg);
bool openSSLServerHandshake(std::string& errMsg);
// Required for OpenSSL < 1.1
static void openSSLLockingCallback(int mode, int type, const char* /*file*/, int /*line*/);
SSL* _ssl_connection;
SSL_CTX* _ssl_context;
const SSL_METHOD* _ssl_method;
mutable std::mutex _mutex; // OpenSSL routines are not thread-safe
SocketTLSOptions _tlsOptions;
mutable std::mutex _mutex; // OpenSSL routines are not thread-safe
static std::once_flag _openSSLInitFlag;
static std::atomic<bool> _openSSLInitializationSuccessful;
};
}
} // namespace ix
#endif // IXWEBSOCKET_USE_OPEN_SSL

View File

@ -1,106 +0,0 @@
/*
* IXSocketSChannel.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*
* See https://docs.microsoft.com/en-us/windows/desktop/WinSock/using-secure-socket-extensions
*
* https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/netds/winsock/securesocket/stcpclient/tcpclient.c
*
* This is the right example to look at:
* https://www.codeproject.com/Articles/1000189/A-Working-TCP-Client-and-Server-With-SSL
*/
#include "IXSocketSChannel.h"
#ifdef _WIN32
# include <basetsd.h>
# include <WinSock2.h>
# include <ws2def.h>
# include <WS2tcpip.h>
# include <schannel.h>
# include <io.h>
#define WIN32_LEAN_AND_MEAN
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <winsock2.h>
#include <mstcpip.h>
#include <ws2tcpip.h>
#include <rpc.h>
#include <ntdsapi.h>
#include <stdio.h>
#include <tchar.h>
#define RECV_DATA_BUF_SIZE 256
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
// link with fwpuclnt.lib for Winsock secure socket extensions
#pragma comment(lib, "fwpuclnt.lib")
// link with ntdsapi.lib for DsMakeSpn function
#pragma comment(lib, "ntdsapi.lib")
// The following function assumes that Winsock
// has already been initialized
#else
# error("This file should only be built on Windows")
#endif
namespace ix
{
SocketSChannel::SocketSChannel()
{
;
}
SocketSChannel::~SocketSChannel()
{
}
bool SocketSChannel::connect(const std::string& host,
int port,
std::string& errMsg)
{
return Socket::connect(host, port, errMsg, nullptr);
}
void SocketSChannel::secureSocket()
{
// there will be a lot to do here ...
}
void SocketSChannel::close()
{
Socket::close();
}
ssize_t SocketSChannel::send(char* buf, size_t nbyte)
{
return Socket::send(buf, nbyte);
}
ssize_t SocketSChannel::send(const std::string& buffer)
{
return Socket::send(buffer);
}
ssize_t SocketSChannel::recv(void* buf, size_t nbyte)
{
return Socket::recv(buf, nbyte);
}
}

View File

@ -1,34 +0,0 @@
/*
* IXSocketSChannel.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSocket.h"
namespace ix
{
class SocketSChannel final : public Socket
{
public:
SocketSChannel();
~SocketSChannel();
virtual bool connect(const std::string& host,
int port,
std::string& errMsg) final;
virtual void close() final;
// The important override
virtual void secureSocket() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t send(const std::string& buffer) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:
};
}

View File

@ -5,37 +5,40 @@
*/
#include "IXSocketServer.h"
#include "IXNetSystem.h"
#include "IXSelectInterrupt.h"
#include "IXSelectInterruptFactory.h"
#include "IXSetThreadName.h"
#include "IXSocket.h"
#include "IXSocketConnect.h"
#include "IXNetSystem.h"
#include <iostream>
#include <sstream>
#include <future>
#include <string.h>
#include "IXSocketFactory.h"
#include <assert.h>
#include <sstream>
#include <stdio.h>
#include <string.h>
namespace ix
{
const int SocketServer::kDefaultPort(8080);
const std::string SocketServer::kDefaultHost("127.0.0.1");
const int SocketServer::kDefaultTcpBacklog(5);
const size_t SocketServer::kDefaultMaxConnections(32);
const size_t SocketServer::kDefaultMaxConnections(128);
const int SocketServer::kDefaultAddressFamily(AF_INET);
SocketServer::SocketServer(int port,
const std::string& host,
int backlog,
size_t maxConnections) :
_port(port),
_host(host),
_backlog(backlog),
_maxConnections(maxConnections),
_serverFd(-1),
_stop(false),
_stopGc(false),
_connectionStateFactory(&ConnectionState::createConnectionState)
SocketServer::SocketServer(
int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
: _port(port)
, _host(host)
, _backlog(backlog)
, _maxConnections(maxConnections)
, _addressFamily(addressFamily)
, _serverFd(-1)
, _stop(false)
, _stopGc(false)
, _connectionStateFactory(&ConnectionState::createConnectionState)
, _acceptSelectInterrupt(createSelectInterrupt())
{
}
SocketServer::~SocketServer()
@ -46,65 +49,112 @@ namespace ix
void SocketServer::logError(const std::string& str)
{
std::lock_guard<std::mutex> lock(_logMutex);
std::cerr << str << std::endl;
fprintf(stderr, "%s\n", str.c_str());
}
void SocketServer::logInfo(const std::string& str)
{
std::lock_guard<std::mutex> lock(_logMutex);
std::cout << str << std::endl;
fprintf(stdout, "%s\n", str.c_str());
}
std::pair<bool, std::string> SocketServer::listen()
{
struct sockaddr_in server; // server address information
// Get a socket for accepting connections.
if ((_serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
std::string acceptSelectInterruptInitErrorMsg;
if (!_acceptSelectInterrupt->init(acceptSelectInterruptInitErrorMsg))
{
std::stringstream ss;
ss << "SocketServer::listen() error creating socket): "
<< strerror(Socket::getErrno());
ss << "SocketServer::listen() error in SelectInterrupt::init: "
<< acceptSelectInterruptInitErrorMsg;
return std::make_pair(false, ss.str());
}
if (_addressFamily != AF_INET && _addressFamily != AF_INET6)
{
std::string errMsg("SocketServer::listen() AF_INET and AF_INET6 are currently "
"the only supported address families");
return std::make_pair(false, errMsg);
}
// Get a socket for accepting connections.
if ((_serverFd = socket(_addressFamily, SOCK_STREAM, 0)) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error creating socket): " << strerror(Socket::getErrno());
return std::make_pair(false, ss.str());
}
// Make that socket reusable. (allow restarting this server at will)
int enable = 1;
if (setsockopt(_serverFd, SOL_SOCKET, SO_REUSEADDR,
(char*) &enable, sizeof(enable)) < 0)
if (setsockopt(_serverFd, SOL_SOCKET, SO_REUSEADDR, (char*) &enable, sizeof(enable)) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling setsockopt(SO_REUSEADDR) "
<< "at address " << _host << ":" << _port
<< " : " << strerror(Socket::getErrno());
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
}
// Bind the socket to the server address.
server.sin_family = AF_INET;
server.sin_port = htons(_port);
// Using INADDR_ANY trigger a pop-up box as binding to any address is detected
// by the osx firewall. We need to codesign the binary with a self-signed cert
// to allow that, but this is a bit of a pain. (this is what node or python would do).
//
// Using INADDR_LOOPBACK also does not work ... while it should.
// We default to 127.0.0.1 (localhost)
//
server.sin_addr.s_addr = inet_addr(_host.c_str());
if (bind(_serverFd, (struct sockaddr *)&server, sizeof(server)) < 0)
if (_addressFamily == AF_INET)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling bind "
<< "at address " << _host << ":" << _port
<< " : " << strerror(Socket::getErrno());
struct sockaddr_in server;
server.sin_family = _addressFamily;
server.sin_port = htons(_port);
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling inet_pton "
<< "at address " << _host << ":" << _port << " : "
<< strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
}
// Bind the socket to the server address.
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling bind "
<< "at address " << _host << ":" << _port << " : "
<< strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
}
}
else // AF_INET6
{
struct sockaddr_in6 server;
server.sin6_family = _addressFamily;
server.sin6_port = htons(_port);
if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling inet_pton "
<< "at address " << _host << ":" << _port << " : "
<< strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
}
// Bind the socket to the server address.
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling bind "
<< "at address " << _host << ":" << _port << " : "
<< strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
}
}
//
@ -114,8 +164,7 @@ namespace ix
{
std::stringstream ss;
ss << "SocketServer::listen() error calling listen "
<< "at address " << _host << ":" << _port
<< " : " << strerror(Socket::getErrno());
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
@ -126,6 +175,8 @@ namespace ix
void SocketServer::start()
{
_stop = false;
if (!_thread.joinable())
{
_thread = std::thread(&SocketServer::run, this);
@ -154,6 +205,12 @@ namespace ix
if (_thread.joinable())
{
_stop = true;
// Wake up select
if (!_acceptSelectInterrupt->notify(SelectInterrupt::kCloseRequest))
{
logError("SocketServer::stop: Cannot wake up from select");
}
_thread.join();
_stop = false;
}
@ -162,6 +219,11 @@ namespace ix
if (_gcThread.joinable())
{
_stopGc = true;
{
std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
_canContinueGC = true;
}
_conditionVariableGC.notify_one();
_gcThread.join();
_stopGc = false;
}
@ -187,7 +249,7 @@ namespace ix
{
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
auto it = _connectionsThreads.begin();
auto itEnd = _connectionsThreads.end();
auto itEnd = _connectionsThreads.end();
while (it != itEnd)
{
@ -210,50 +272,55 @@ namespace ix
// Set the socket to non blocking mode, so that accept calls are not blocking
SocketConnect::configure(_serverFd);
// Use a cryptic name to stay within the 16 bytes limit thread name limitation
// $ echo Srv:gc:64000 | wc -c
// 13
setThreadName("Srv:ac:" + std::to_string(_port));
for (;;)
{
if (_stop) return;
// Use select to check whether a new connection is in progress
fd_set rfds;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10 * 1000; // 10ms timeout
// Use poll to check whether a new connection is in progress
int timeoutMs = -1;
#ifdef _WIN32
// select cannot be interrupted on Windows so we need to pass a small timeout
timeoutMs = 10;
#endif
FD_ZERO(&rfds);
FD_SET(_serverFd, &rfds);
bool readyToRead = true;
PollResultType pollResult =
Socket::poll(readyToRead, timeoutMs, _serverFd, _acceptSelectInterrupt);
if (select(_serverFd + 1, &rfds, nullptr, nullptr, &timeout) < 0 &&
(errno == EBADF || errno == EINVAL))
if (pollResult == PollResultType::Error)
{
std::stringstream ss;
ss << "SocketServer::run() error in select: "
<< strerror(Socket::getErrno());
ss << "SocketServer::run() error in select: " << strerror(Socket::getErrno());
logError(ss.str());
continue;
}
if (!FD_ISSET(_serverFd, &rfds))
if (pollResult != PollResultType::ReadyForRead)
{
// We reached the select timeout, and no new connections are pending
continue;
}
// Accept a connection.
// FIXME: Is this working for ipv6 ?
struct sockaddr_in client; // client address information
int clientFd; // socket connected to client
socklen_t addressLen = sizeof(client);
memset(&client, 0, sizeof(client));
if ((clientFd = accept(_serverFd, (struct sockaddr *)&client, &addressLen)) < 0)
if ((clientFd = accept(_serverFd, (struct sockaddr*) &client, &addressLen)) < 0)
{
if (!Socket::isWaitNeeded())
{
// FIXME: that error should be propagated
int err = Socket::getErrno();
std::stringstream ss;
ss << "SocketServer::run() error accepting connection: "
<< err << ", " << strerror(err);
ss << "SocketServer::run() error accepting connection: " << err << ", "
<< strerror(err);
logError(ss.str());
}
continue;
@ -262,8 +329,7 @@ namespace ix
if (getConnectedClientsCount() >= _maxConnections)
{
std::stringstream ss;
ss << "SocketServer::run() reached max connections = "
<< _maxConnections << ". "
ss << "SocketServer::run() reached max connections = " << _maxConnections << ". "
<< "Not accepting connection";
logError(ss.str());
@ -272,22 +338,89 @@ namespace ix
continue;
}
// Retrieve connection info, the ip address of the remote peer/client)
std::string remoteIp;
int remotePort;
if (_addressFamily == AF_INET)
{
char remoteIp4[INET_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr)
{
int err = Socket::getErrno();
std::stringstream ss;
ss << "SocketServer::run() error calling inet_ntop (ipv4): " << err << ", "
<< strerror(err);
logError(ss.str());
Socket::closeSocket(clientFd);
continue;
}
remotePort = ix::network_to_host_short(client.sin_port);
remoteIp = remoteIp4;
}
else // AF_INET6
{
char remoteIp6[INET6_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) ==
nullptr)
{
int err = Socket::getErrno();
std::stringstream ss;
ss << "SocketServer::run() error calling inet_ntop (ipv6): " << err << ", "
<< strerror(err);
logError(ss.str());
Socket::closeSocket(clientFd);
continue;
}
remotePort = ix::network_to_host_short(client.sin_port);
remoteIp = remoteIp6;
}
std::shared_ptr<ConnectionState> connectionState;
if (_connectionStateFactory)
{
connectionState = _connectionStateFactory();
}
connectionState->setOnSetTerminatedCallback([this] { onSetTerminatedCallback(); });
connectionState->setRemoteIp(remoteIp);
connectionState->setRemotePort(remotePort);
if (_stop) return;
// create socket
std::string errorMsg;
bool tls = _socketTLSOptions.tls;
auto socket = createSocket(tls, clientFd, errorMsg, _socketTLSOptions);
if (socket == nullptr)
{
logError("SocketServer::run() cannot create socket: " + errorMsg);
Socket::closeSocket(clientFd);
continue;
}
// Set the socket to non blocking mode + other tweaks
SocketConnect::configure(clientFd);
if (!socket->accept(errorMsg))
{
logError("SocketServer::run() tls accept failed: " + errorMsg);
Socket::closeSocket(clientFd);
continue;
}
// Launch the handleConnection work asynchronously in its own thread.
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
_connectionsThreads.push_back(std::make_pair(
connectionState,
std::thread(&SocketServer::handleConnection,
this,
clientFd,
connectionState)));
connectionState,
std::thread(
&SocketServer::handleConnection, this, std::move(socket), connectionState)));
}
}
@ -299,6 +432,11 @@ namespace ix
void SocketServer::runGC()
{
// Use a cryptic name to stay within the 16 bytes limit thread name limitation
// $ echo Srv:gc:64000 | wc -c
// 13
setThreadName("Srv:gc:" + std::to_string(_port));
for (;;)
{
// Garbage collection to shutdown/join threads for closed connections.
@ -311,9 +449,58 @@ namespace ix
break;
}
// Sleep a little bit then keep cleaning up
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// Unless we are stopping the server, wait for a connection
// to be terminated to run the threads GC, instead of busy waiting
// with a sleep
if (!_stopGc)
{
std::unique_lock<std::mutex> lock(_conditionVariableMutexGC);
if(!_canContinueGC) {
_conditionVariableGC.wait(lock, [this]{ return _canContinueGC; });
}
_canContinueGC = false;
}
}
}
}
void SocketServer::setTLSOptions(const SocketTLSOptions& socketTLSOptions)
{
_socketTLSOptions = socketTLSOptions;
}
void SocketServer::onSetTerminatedCallback()
{
// a connection got terminated, we can run the connection thread GC,
// so wake up the thread responsible for that
{
std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
_canContinueGC = true;
}
_conditionVariableGC.notify_one();
}
int SocketServer::getPort()
{
return _port;
}
std::string SocketServer::getHost()
{
return _host;
}
int SocketServer::getBacklog()
{
return _backlog;
}
std::size_t SocketServer::getMaxConnections()
{
return _maxConnections;
}
int SocketServer::getAddressFamily()
{
return _addressFamily;
}
} // namespace ix

View File

@ -7,33 +7,39 @@
#pragma once
#include "IXConnectionState.h"
#include <utility> // pair
#include <string>
#include <set>
#include <thread>
#include <list>
#include <mutex>
#include <functional>
#include <memory>
#include "IXNetSystem.h"
#include "IXSelectInterrupt.h"
#include "IXSocketTLSOptions.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <utility> // pair
namespace ix
{
class SocketServer {
class Socket;
class SocketServer
{
public:
using ConnectionStateFactory = std::function<std::shared_ptr<ConnectionState>()>;
// Each connection is handled by its own worker thread.
// We use a list as we only care about remove and append operations.
using ConnectionThreads = std::list<std::pair<std::shared_ptr<ConnectionState>,
std::thread>>;
using ConnectionThreads =
std::list<std::pair<std::shared_ptr<ConnectionState>, std::thread>>;
SocketServer(int port = SocketServer::kDefaultPort,
const std::string& host = SocketServer::kDefaultHost,
int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections);
size_t maxConnections = SocketServer::kDefaultMaxConnections,
int addressFamily = SocketServer::kDefaultAddressFamily);
virtual ~SocketServer();
virtual void stop();
@ -46,13 +52,20 @@ namespace ix
const static std::string kDefaultHost;
const static int kDefaultTcpBacklog;
const static size_t kDefaultMaxConnections;
const static int kDefaultAddressFamily;
void start();
std::pair<bool, std::string> listen();
void wait();
protected:
void setTLSOptions(const SocketTLSOptions& socketTLSOptions);
int getPort();
std::string getHost();
int getBacklog();
std::size_t getMaxConnections();
int getAddressFamily();
protected:
// Logging
void logError(const std::string& str);
void logInfo(const std::string& str);
@ -65,16 +78,19 @@ namespace ix
std::string _host;
int _backlog;
size_t _maxConnections;
int _addressFamily;
// socket for accepting connections
int _serverFd;
socket_t _serverFd;
std::atomic<bool> _stop;
std::mutex _logMutex;
// background thread to wait for incoming connections
std::atomic<bool> _stop;
std::thread _thread;
void run();
void onSetTerminatedCallback();
// background thread to cleanup (join) terminated threads
std::atomic<bool> _stopGc;
@ -93,12 +109,23 @@ namespace ix
// the factory to create ConnectionState objects
ConnectionStateFactory _connectionStateFactory;
virtual void handleConnection(int fd,
virtual void handleConnection(std::unique_ptr<Socket>,
std::shared_ptr<ConnectionState> connectionState) = 0;
virtual size_t getConnectedClientsCount() = 0;
// Returns true if all connection threads are joined
void closeTerminatedThreads();
size_t getConnectionsThreadsCount();
SocketTLSOptions _socketTLSOptions;
// to wake up from select
SelectInterruptPtr _acceptSelectInterrupt;
// used by the gc thread, to know that a thread needs to be garbage collected
// as a connection
std::condition_variable _conditionVariableGC;
std::mutex _conditionVariableMutexGC;
bool _canContinueGC{ false };
};
}
} // namespace ix

View File

@ -0,0 +1,93 @@
/*
* IXSocketTLSOptions.h
* Author: Matt DeBoer
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#include "IXSocketTLSOptions.h"
#include <assert.h>
#include <fstream>
#include <sstream>
namespace ix
{
const char* kTLSCAFileUseSystemDefaults = "SYSTEM";
const char* kTLSCAFileDisableVerify = "NONE";
const char* kTLSCiphersUseDefault = "DEFAULT";
const char* kTLSInMemoryMarker = "-----BEGIN CERTIFICATE-----";
bool SocketTLSOptions::isValid() const
{
if (!_validated)
{
if (!certFile.empty() && !std::ifstream(certFile))
{
_errMsg = "certFile not found: " + certFile;
return false;
}
if (!keyFile.empty() && !std::ifstream(keyFile))
{
_errMsg = "keyFile not found: " + keyFile;
return false;
}
if (!caFile.empty() && caFile != kTLSCAFileDisableVerify &&
caFile != kTLSCAFileUseSystemDefaults && !std::ifstream(caFile))
{
_errMsg = "caFile not found: " + caFile;
return false;
}
if (certFile.empty() != keyFile.empty())
{
_errMsg = "certFile and keyFile must be both present, or both absent";
return false;
}
_validated = true;
}
return true;
}
bool SocketTLSOptions::hasCertAndKey() const
{
return !certFile.empty() && !keyFile.empty();
}
bool SocketTLSOptions::isUsingSystemDefaults() const
{
return caFile == kTLSCAFileUseSystemDefaults;
}
bool SocketTLSOptions::isUsingInMemoryCAs() const
{
return caFile.find(kTLSInMemoryMarker) != std::string::npos;
}
bool SocketTLSOptions::isPeerVerifyDisabled() const
{
return caFile == kTLSCAFileDisableVerify;
}
bool SocketTLSOptions::isUsingDefaultCiphers() const
{
return ciphers.empty() || ciphers == kTLSCiphersUseDefault;
}
const std::string& SocketTLSOptions::getErrorMsg() const
{
return _errMsg;
}
std::string SocketTLSOptions::getDescription() const
{
std::stringstream ss;
ss << "TLS Options:" << std::endl;
ss << " certFile = " << certFile << std::endl;
ss << " keyFile = " << keyFile << std::endl;
ss << " caFile = " << caFile << std::endl;
ss << " ciphers = " << ciphers << std::endl;
ss << " tls = " << tls << std::endl;
return ss.str();
}
} // namespace ix

View File

@ -0,0 +1,57 @@
/*
* IXSocketTLSOptions.h
* Author: Matt DeBoer
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
struct SocketTLSOptions
{
public:
// check validity of the object
bool isValid() const;
// the certificate presented to peers
std::string certFile;
// the key used for signing/encryption
std::string keyFile;
// the ca certificate (or certificate bundle) file containing
// certificates to be trusted by peers; use 'SYSTEM' to
// leverage the system defaults, use 'NONE' to disable peer verification
std::string caFile = "SYSTEM";
// list of ciphers (rsa, etc...)
std::string ciphers = "DEFAULT";
// whether tls is enabled, used for server code
bool tls = false;
// whether to skip validating the peer's hostname against the certificate presented
bool disable_hostname_validation = false;
bool hasCertAndKey() const;
bool isUsingSystemDefaults() const;
bool isUsingInMemoryCAs() const;
bool isPeerVerifyDisabled() const;
bool isUsingDefaultCiphers() const;
const std::string& getErrorMsg() const;
std::string getDescription() const;
private:
mutable std::string _errMsg;
mutable bool _validated = false;
};
} // namespace ix

Some files were not shown because too many files have changed in this diff Show More