Compare commits

..

1 Commits

Author SHA1 Message Date
e31b913280 docker debugging 2021-02-15 11:16:55 -08:00
40 changed files with 169 additions and 557 deletions

View File

@ -21,7 +21,5 @@ jobs:
pip install pygments pip install pygments
- name: Build doc - name: Build doc
run: | run: |
git clean -dfx .
git fetch
git pull git pull
mkdocs gh-deploy mkdocs gh-deploy

View File

@ -14,7 +14,7 @@ jobs:
- run: | - run: |
mkdir build mkdir build
cd build cd build
cmake -GNinja -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=OFF -DBUILD_SHARED_LIBS=OFF .. cmake -GNinja -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 ..
- run: | - run: |
cd build cd build
ninja ninja

View File

@ -1,27 +0,0 @@
name: windows_gcc
on:
push:
paths-ignore:
- 'docs/**'
jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: egor-tensin/setup-mingw@v2
- 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

1
.gitignore vendored
View File

@ -7,4 +7,3 @@ ws/.certs/
ws/.srl ws/.srl
ixhttpd ixhttpd
makefile makefile
a.out

View File

@ -12,14 +12,12 @@ set (CMAKE_CXX_STANDARD 11)
set (CXX_STANDARD_REQUIRED ON) set (CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF) set (CMAKE_CXX_EXTENSIONS OFF)
option (BUILD_DEMO OFF)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif() endif()
if (UNIX) if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -gz")
endif() endif()
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
@ -88,7 +86,6 @@ set( IXWEBSOCKET_HEADERS
ixwebsocket/IXSocketTLSOptions.h ixwebsocket/IXSocketTLSOptions.h
ixwebsocket/IXStrCaseCompare.h ixwebsocket/IXStrCaseCompare.h
ixwebsocket/IXUdpSocket.h ixwebsocket/IXUdpSocket.h
ixwebsocket/IXUniquePtr.h
ixwebsocket/IXUrlParser.h ixwebsocket/IXUrlParser.h
ixwebsocket/IXUuid.h ixwebsocket/IXUuid.h
ixwebsocket/IXUtf8Validator.h ixwebsocket/IXUtf8Validator.h
@ -114,7 +111,6 @@ set( IXWEBSOCKET_HEADERS
ixwebsocket/IXWebSocketVersion.h ixwebsocket/IXWebSocketVersion.h
) )
option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF)
option(USE_TLS "Enable TLS support" FALSE) option(USE_TLS "Enable TLS support" FALSE)
if (USE_TLS) if (USE_TLS)
@ -148,7 +144,7 @@ if (USE_TLS)
endif() endif()
endif() endif()
add_library( ixwebsocket add_library( ixwebsocket STATIC
${IXWEBSOCKET_SOURCES} ${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS} ${IXWEBSOCKET_HEADERS}
) )
@ -195,7 +191,7 @@ if (USE_TLS)
target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES}) target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES})
elseif (USE_SECURE_TRANSPORT) elseif (USE_SECURE_TRANSPORT)
message(STATUS "TLS configured to use secure transport") message(STATUS "TLS configured to use secure transport")
target_link_libraries(ixwebsocket "-framework Foundation" "-framework Security") target_link_libraries(ixwebsocket "-framework foundation" "-framework security")
endif() endif()
endif() endif()
@ -277,8 +273,3 @@ if (USE_WS OR USE_TEST)
add_subdirectory(test) add_subdirectory(test)
endif() endif()
endif() endif()
if (BUILD_DEMO)
add_executable(demo main.cpp)
target_link_libraries(demo ixwebsocket)
endif()

View File

@ -15,16 +15,13 @@ A bad security bug affecting users compiling with SSL enabled and OpenSSL as the
* Super simple standalone example. See ws folder, unittest and doc/usage.md for more. * Super simple standalone example. See ws folder, unittest and doc/usage.md for more.
* *
* On macOS * On macOS
* $ mkdir -p build ; (cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install) * $ 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 * $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
* $ ./a.out * $ ./a.out
*
* Or use cmake -DBUILD_DEMO=ON option for other platforms
*/ */
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXUserAgent.h>
#include <iostream> #include <iostream>
int main() int main()
@ -35,8 +32,6 @@ int main()
// Our websocket object // Our websocket object
ix::WebSocket webSocket; 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"); std::string url("wss://echo.websocket.org");
webSocket.setUrl(url); webSocket.setUrl(url);
@ -49,18 +44,10 @@ int main()
if (msg->type == ix::WebSocketMessageType::Message) if (msg->type == ix::WebSocketMessageType::Message)
{ {
std::cout << "received message: " << msg->str << std::endl; std::cout << "received message: " << msg->str << std::endl;
std::cout << "> " << std::flush;
} }
else if (msg->type == ix::WebSocketMessageType::Open) else if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::cout << "Connection established" << std::endl; 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;
} }
} }
); );
@ -71,16 +58,13 @@ int main()
// Send a message to the server (default to TEXT mode) // Send a message to the server (default to TEXT mode)
webSocket.send("hello world"); webSocket.send("hello world");
// Display a prompt while (true)
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::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
webSocket.send(text);
} }
return 0; return 0;
@ -105,7 +89,6 @@ If your company or project is using this library, feel free to open an issue or
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod - [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 - [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 - [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 ## Alternative libraries
@ -115,7 +98,6 @@ There are plenty of great websocket libraries out there, which might work for yo
* [beast](https://github.com/boostorg/beast) - C++ * [beast](https://github.com/boostorg/beast) - C++
* [libwebsockets](https://libwebsockets.org/) - C * [libwebsockets](https://libwebsockets.org/) - C
* [µWebSockets](https://github.com/uNetworking/uWebSockets) - 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. [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.

7
build.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/sh
rm -rf build
mkdir -p build
cd build
cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=OFF ..
ninja

View File

@ -1,39 +1,25 @@
FROM alpine:3.12 as build # Build time
#
# Build with
# docker build --ssh default -t ws .
#
# focal == ubuntu 2020.04
# groovy == ubuntu 2020.10
FROM ubuntu:groovy
RUN apk add --no-cache \ RUN apt update
gcc g++ musl-dev linux-headers \
cmake mbedtls-dev make zlib-dev python3-dev ninja git
RUN addgroup -S app && \ RUN apt-get -y install g++ cmake make automake ccache libtool flex bison pkg-config git python3 jq
adduser -S -G app app && \ RUN apt-get -y install libjemalloc-dev libssl-dev libmcrypt-dev mcrypt zlib1g lua5.1-dev uuid-dev libz-dev binutils-dev
chown -R app:app /opt && \ RUN apt-get -y install libboost-dev libboost-test-dev libboost-program-options-dev libboost-all-dev libboost-regex-dev
chown -R app:app /usr/local RUN apt-get -y install ninja-build
# There is a bug in CMake where we cannot build from the root top folder COPY . /opt
# So we build from /opt
COPY --chown=app:app . /opt
WORKDIR /opt WORKDIR /opt
USER app RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN make -f makefile.dev ws_mbedtls_install && \ RUN rm -rf build
sh tools/trim_repo_for_docker.sh RUN --mount=type=ssh ./build.sh
FROM alpine:3.12 as runtime COPY /opt/build/ws/ws /usr/local/bin/ws
CMD ["/usr/local/bin/ws"]
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

View File

@ -2,58 +2,6 @@
All changes to this project will be documented in this file. All changes to this project will be documented in this file.
## [11.2.6] - 2021-05-18
(Windows) move EINVAL (re)definition from IXSocket.h to IXNetSystem.h (fix #289)
## [11.2.5] - 2021-04-04
(http client) DEL is not an HTTP method name, but DELETE is
## [11.2.4] - 2021-03-25
(cmake) install IXUniquePtr.h
## [11.2.3] - 2021-03-24
(ssl + windows) missing include for CertOpenStore function
## [11.2.2] - 2021-03-23
(ixwebsocket) version bump
## [11.2.1] - 2021-03-23
(ixwebsocket) version bump
## [11.2.0] - 2021-03-23
(ixwebsocket) correct mingw support (gcc on windows)
## [11.1.4] - 2021-03-23
(ixwebsocket) add getMinWaitBetweenReconnectionRetries
## [11.1.3] - 2021-03-23
(ixwebsocket) New option to set the min wait between reconnection attempts. Still default to 1ms. (setMinWaitBetweenReconnectionRetries).
## [11.1.2] - 2021-03-22
(ws) initialize maxWaitBetweenReconnectionRetries to a non zero value ; a zero value was causing spurious reconnections attempts
## [11.1.1] - 2021-03-20
(cmake) Library can be built as a static or a dynamic library, controlled with BUILD_SHARED_LIBS. Default to static library
## [11.1.0] - 2021-03-16
(ixwebsocket) Use LEAN_AND_MEAN Windows define to help with undefined link error when building a DLL. Support websocket server disablePerMessageDeflate option correctly.
## [11.0.9] - 2021-03-07
(ixwebsocket) Expose setHandshakeTimeout method
## [11.0.8] - 2020-12-25 ## [11.0.8] - 2020-12-25
(ws) trim ws dependencies no more ixcrypto and ixcore deps (ws) trim ws dependencies no more ixcrypto and ixcore deps

View File

@ -17,13 +17,13 @@ There is a unittest which can be executed by typing `make test`.
Options for building: 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_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension
* `-DUSE_TLS=1` will enable TLS support * `-DUSE_TLS=1` will enable TLS support
* `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows) * `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support * `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
* `-DUSE_WS=1` will build the ws interactive command line tool * `-DUSE_WS=1` will build the ws interactive command line tool
* `-DUSE_TEST=1` will build the unittest * `-DUSE_TEST=1` will build the unittest
* `-DUSE_PYTHON=1` will use Python3 for cobra bots, require Python3 to be installed.
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. 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.

View File

@ -256,24 +256,11 @@ Wait time(ms): 6400
Wait time(ms): 10000 Wait time(ms): 10000
``` ```
The waiting time is capped by default at 10s between 2 attempts, but that value The waiting time is capped by default at 10s between 2 attempts, but that value can be changed and queried.
can be changed and queried. The minimum waiting time can also be set.
```cpp ```cpp
webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s
uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries(); 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 ## WebSocket server API
@ -347,10 +334,6 @@ if (!res.first)
return 1; 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() // Run the server in the background. Server can be stoped by calling server.stop()
server.start(); server.start();
@ -374,10 +357,13 @@ The webSocket reference is guaranteed to be always valid ; by design the callbac
// Bound host name, max connections and listen backlog can also be passed in as parameters. // Bound host name, max connections and listen backlog can also be passed in as parameters.
ix::WebSocketServer server(port); ix::WebSocketServer server(port);
server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) { server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionState,
WebSocket& webSocket,
const WebSocketMessagePtr& msg)
{
// The ConnectionState object contains information about the connection, // The ConnectionState object contains information about the connection,
// at this point only the client ip address and the port. // at this point only the client ip address and the port.
std::cout << "Remote ip: " << connectionState->getRemoteIp() << std::endl; std::cout << "Remote ip: " << connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open) if (msg->type == ix::WebSocketMessageType::Open)
{ {
@ -395,7 +381,7 @@ server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connec
std::cout << "Headers:" << std::endl; std::cout << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers) for (auto it : msg->openInfo.headers)
{ {
std::cout << "\t" << it.first << ": " << it.second << std::endl; std::cout << it.first << ": " << it.second << std::endl;
} }
} }
else if (msg->type == ix::WebSocketMessageType::Message) else if (msg->type == ix::WebSocketMessageType::Message)
@ -404,11 +390,9 @@ server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connec
// All connected clients are available in an std::set. See the broadcast cpp example. // 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. // 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. // 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); webSocket.send(msg->str, msg->binary);
} }
}); );
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -417,10 +401,6 @@ if (!res.first)
return 1; 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() // Run the server in the background. Server can be stoped by calling server.stop()
server.start(); server.start();

View File

@ -24,12 +24,6 @@
#include <string.h> #include <string.h>
#include <thread> #include <thread>
// mingw build quirks
#if defined(_WIN32) && defined(__GNUC__)
#define AI_NUMERICSERV NI_NUMERICSERV
#define AI_ADDRCONFIG LUP_ADDRCONFIG
#endif
namespace ix namespace ix
{ {
const int64_t DNSLookup::kDefaultWait = 1; // ms const int64_t DNSLookup::kDefaultWait = 1; // ms

View File

@ -10,22 +10,16 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount, uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
uint32_t maxWaitBetweenReconnectionRetries, uint32_t maxWaitBetweenReconnectionRetries)
uint32_t minWaitBetweenReconnectionRetries)
{ {
uint32_t waitTime = (retryCount < 26) ? (std::pow(2, retryCount) * 100) : 0; uint32_t wait_time = (retry_count < 26) ? (std::pow(2, retry_count) * 100) : 0;
if (waitTime < minWaitBetweenReconnectionRetries) if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0)
{ {
waitTime = minWaitBetweenReconnectionRetries; wait_time = maxWaitBetweenReconnectionRetries;
} }
if (waitTime > maxWaitBetweenReconnectionRetries || waitTime == 0) return wait_time;
{
waitTime = maxWaitBetweenReconnectionRetries;
}
return waitTime;
} }
} // namespace ix } // namespace ix

View File

@ -10,7 +10,6 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount, uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
uint32_t maxWaitBetweenReconnectionRetries, uint32_t maxWaitBetweenReconnectionRetries);
uint32_t minWaitBetweenReconnectionRetries);
} // namespace ix } // namespace ix

View File

@ -137,7 +137,7 @@ namespace ix
{ {
contentLength = std::stoi(headers["Content-Length"]); contentLength = std::stoi(headers["Content-Length"]);
} }
catch(const std::exception&) catch (std::exception)
{ {
return std::make_tuple( return std::make_tuple(
false, "Error parsing HTTP Header 'Content-Length'", httpRequest); false, "Error parsing HTTP Header 'Content-Length'", httpRequest);

View File

@ -20,11 +20,10 @@
namespace ix namespace ix
{ {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
const std::string HttpClient::kPost = "POST"; const std::string HttpClient::kPost = "POST";
const std::string HttpClient::kGet = "GET"; const std::string HttpClient::kGet = "GET";
const std::string HttpClient::kHead = "HEAD"; const std::string HttpClient::kHead = "HEAD";
const std::string HttpClient::kDelete = "DELETE"; const std::string HttpClient::kDel = "DEL";
const std::string HttpClient::kPut = "PUT"; const std::string HttpClient::kPut = "PUT";
const std::string HttpClient::kPatch = "PATCH"; const std::string HttpClient::kPatch = "PATCH";
@ -558,9 +557,9 @@ namespace ix
return request(url, kHead, std::string(), args); return request(url, kHead, std::string(), args);
} }
HttpResponsePtr HttpClient::Delete(const std::string& url, HttpRequestArgsPtr args) HttpResponsePtr HttpClient::del(const std::string& url, HttpRequestArgsPtr args)
{ {
return request(url, kDelete, std::string(), args); return request(url, kDel, std::string(), args);
} }
HttpResponsePtr HttpClient::request(const std::string& url, HttpResponsePtr HttpClient::request(const std::string& url,

View File

@ -30,7 +30,7 @@ namespace ix
HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args); HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args); HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr Delete(const std::string& url, HttpRequestArgsPtr args); HttpResponsePtr del(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr post(const std::string& url, HttpResponsePtr post(const std::string& url,
const HttpParameters& httpParameters, const HttpParameters& httpParameters,
@ -94,7 +94,7 @@ namespace ix
const static std::string kPost; const static std::string kPost;
const static std::string kGet; const static std::string kGet;
const static std::string kHead; const static std::string kHead;
const static std::string kDelete; const static std::string kDel;
const static std::string kPut; const static std::string kPut;
const static std::string kPatch; const static std::string kPatch;

View File

@ -124,158 +124,4 @@ namespace ix
#endif #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) < 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
}
} // namespace ix } // namespace ix

View File

@ -7,49 +7,15 @@
#pragma once #pragma once
#ifdef _WIN32 #ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <WS2tcpip.h> #include <WS2tcpip.h>
#include <WinSock2.h> #include <WinSock2.h>
#include <basetsd.h> #include <basetsd.h>
#include <io.h> #include <io.h>
#include <ws2def.h> #include <ws2def.h>
#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 // Define our own poll on Windows, as a wrapper on top of select
typedef unsigned long int nfds_t; typedef unsigned long int nfds_t;
// mingw does not know about poll so mock it
#if defined(__GNUC__)
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 #else
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
@ -78,7 +44,4 @@ namespace ix
bool uninitNetSystem(); bool uninitNetSystem();
int poll(struct pollfd* fds, nfds_t nfds, int timeout); int poll(struct pollfd* fds, nfds_t nfds, int timeout);
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
int inet_pton(int af, const char* src, void* dst);
} // namespace ix } // namespace ix

View File

@ -37,7 +37,6 @@ namespace ix
void SetThreadName(DWORD dwThreadID, const char* threadName) void SetThreadName(DWORD dwThreadID, const char* threadName)
{ {
#ifndef __GNUC__
THREADNAME_INFO info; THREADNAME_INFO info;
info.dwType = 0x1000; info.dwType = 0x1000;
info.szName = threadName; info.szName = threadName;
@ -52,7 +51,6 @@ namespace ix
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
} }
#endif
} }
#endif #endif

View File

@ -15,6 +15,20 @@
#ifdef _WIN32 #ifdef _WIN32
#include <BaseTsd.h> #include <BaseTsd.h>
typedef SSIZE_T ssize_t; 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 "IXCancellationRequest.h"

View File

@ -16,11 +16,6 @@
#include "IXSocketConnect.h" #include "IXSocketConnect.h"
#include <string.h> #include <string.h>
#ifdef _WIN32
// For manipulating the certificate store
#include <wincrypt.h>
#endif
namespace ix namespace ix
{ {
SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd) SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd)

View File

@ -24,11 +24,6 @@
#endif #endif
#define socketerrno errno #define socketerrno errno
#ifdef _WIN32
// For manipulating the certificate store
#include <wincrypt.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
namespace namespace
{ {

View File

@ -104,7 +104,7 @@ namespace ix
server.sin_family = _addressFamily; server.sin_family = _addressFamily;
server.sin_port = htons(_port); server.sin_port = htons(_port);
if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0) if (inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error calling inet_pton " ss << "SocketServer::listen() error calling inet_pton "
@ -133,7 +133,7 @@ namespace ix
server.sin6_family = _addressFamily; server.sin6_family = _addressFamily;
server.sin6_port = htons(_port); server.sin6_port = htons(_port);
if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0) if (inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error calling inet_pton " ss << "SocketServer::listen() error calling inet_pton "
@ -338,7 +338,7 @@ namespace ix
if (_addressFamily == AF_INET) if (_addressFamily == AF_INET)
{ {
char remoteIp4[INET_ADDRSTRLEN]; char remoteIp4[INET_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr) if (inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr)
{ {
int err = Socket::getErrno(); int err = Socket::getErrno();
std::stringstream ss; std::stringstream ss;
@ -357,8 +357,7 @@ namespace ix
else // AF_INET6 else // AF_INET6
{ {
char remoteIp6[INET6_ADDRSTRLEN]; char remoteIp6[INET6_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == nullptr)
nullptr)
{ {
int err = Socket::getErrno(); int err = Socket::getErrno();
std::stringstream ss; std::stringstream ss;

View File

@ -14,7 +14,7 @@ namespace ix
bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1, bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1,
const unsigned char& c2) const const unsigned char& c2) const
{ {
#if defined(_WIN32) && !defined(__GNUC__) #ifdef _WIN32
return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale()); return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale());
#else #else
return std::tolower(c1) < std::tolower(c2); return std::tolower(c1) < std::tolower(c2);

View File

@ -22,14 +22,12 @@ namespace ix
const int WebSocket::kDefaultPingIntervalSecs(-1); const int WebSocket::kDefaultPingIntervalSecs(-1);
const bool WebSocket::kDefaultEnablePong(true); const bool WebSocket::kDefaultEnablePong(true);
const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s
const uint32_t WebSocket::kDefaultMinWaitBetweenReconnectionRetries(1); // 1 ms
WebSocket::WebSocket() WebSocket::WebSocket()
: _onMessageCallback(OnMessageCallback()) : _onMessageCallback(OnMessageCallback())
, _stop(false) , _stop(false)
, _automaticReconnection(true) , _automaticReconnection(true)
, _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries) , _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries)
, _minWaitBetweenReconnectionRetries(kDefaultMinWaitBetweenReconnectionRetries)
, _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs)
, _enablePong(kDefaultEnablePong) , _enablePong(kDefaultEnablePong)
, _pingIntervalSecs(kDefaultPingIntervalSecs) , _pingIntervalSecs(kDefaultPingIntervalSecs)
@ -58,11 +56,6 @@ namespace ix
_url = url; _url = url;
} }
void WebSocket::setHandshakeTimeout(int handshakeTimeoutSecs)
{
_handshakeTimeoutSecs = handshakeTimeoutSecs;
}
void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers) void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers)
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
@ -138,24 +131,12 @@ namespace ix
_maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries; _maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries;
} }
void WebSocket::setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries)
{
std::lock_guard<std::mutex> lock(_configMutex);
_minWaitBetweenReconnectionRetries = minWaitBetweenReconnectionRetries;
}
uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
return _maxWaitBetweenReconnectionRetries; return _maxWaitBetweenReconnectionRetries;
} }
uint32_t WebSocket::getMinWaitBetweenReconnectionRetries() const
{
std::lock_guard<std::mutex> lock(_configMutex);
return _minWaitBetweenReconnectionRetries;
}
void WebSocket::start() void WebSocket::start()
{ {
if (_thread.joinable()) return; // we've already been started if (_thread.joinable()) return; // we've already been started
@ -232,9 +213,7 @@ namespace ix
return status; return status;
} }
WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs)
int timeoutSecs,
bool enablePerMessageDeflate)
{ {
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
@ -242,8 +221,7 @@ namespace ix
_perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs); _perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs);
} }
WebSocketInitResult status = WebSocketInitResult status = _ws.connectToSocket(std::move(socket), timeoutSecs);
_ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate);
if (!status.success) if (!status.success)
{ {
return status; return status;
@ -325,10 +303,8 @@ namespace ix
if (_automaticReconnection) if (_automaticReconnection)
{ {
duration = duration = millis(calculateRetryWaitMilliseconds(
millis(calculateRetryWaitMilliseconds(retries++, retries++, _maxWaitBetweenReconnectionRetries));
_maxWaitBetweenReconnectionRetries,
_minWaitBetweenReconnectionRetries));
connectErr.wait_time = duration.count(); connectErr.wait_time = duration.count();
connectErr.retries = retries; connectErr.retries = retries;

View File

@ -58,7 +58,6 @@ namespace ix
void enablePerMessageDeflate(); void enablePerMessageDeflate();
void disablePerMessageDeflate(); void disablePerMessageDeflate();
void addSubProtocol(const std::string& subProtocol); void addSubProtocol(const std::string& subProtocol);
void setHandshakeTimeout(int handshakeTimeoutSecs);
// Run asynchronously, by calling start and stop. // Run asynchronously, by calling start and stop.
void start(); void start();
@ -101,9 +100,7 @@ namespace ix
void disableAutomaticReconnection(); void disableAutomaticReconnection();
bool isAutomaticReconnectionEnabled() const; bool isAutomaticReconnectionEnabled() const;
void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries); void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries);
void setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries);
uint32_t getMaxWaitBetweenReconnectionRetries() const; uint32_t getMaxWaitBetweenReconnectionRetries() const;
uint32_t getMinWaitBetweenReconnectionRetries() const;
const std::vector<std::string>& getSubProtocols(); const std::vector<std::string>& getSubProtocols();
private: private:
@ -117,9 +114,7 @@ namespace ix
static void invokeTrafficTrackerCallback(size_t size, bool incoming); static void invokeTrafficTrackerCallback(size_t size, bool incoming);
// Server // Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, int timeoutSecs);
int timeoutSecs,
bool enablePerMessageDeflate);
WebSocketTransport _ws; WebSocketTransport _ws;
@ -142,9 +137,7 @@ namespace ix
// Automatic reconnection // Automatic reconnection
std::atomic<bool> _automaticReconnection; std::atomic<bool> _automaticReconnection;
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries; static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
static const uint32_t kDefaultMinWaitBetweenReconnectionRetries;
uint32_t _maxWaitBetweenReconnectionRetries; uint32_t _maxWaitBetweenReconnectionRetries;
uint32_t _minWaitBetweenReconnectionRetries;
// Make the sleeping in the automatic reconnection cancellable // Make the sleeping in the automatic reconnection cancellable
std::mutex _sleepMutex; std::mutex _sleepMutex;

View File

@ -241,8 +241,7 @@ namespace ix
return WebSocketInitResult(true, status, "", headers, path); return WebSocketInitResult(true, status, "", headers, path);
} }
WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs, WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs)
bool enablePerMessageDeflate)
{ {
_requestInitCancellation = false; _requestInitCancellation = false;
@ -339,7 +338,7 @@ namespace ix
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header); WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header);
// If the client has requested that extension, // If the client has requested that extension,
if (webSocketPerMessageDeflateOptions.enabled() && enablePerMessageDeflate) if (webSocketPerMessageDeflateOptions.enabled())
{ {
_enablePerMessageDeflate = true; _enablePerMessageDeflate = true;

View File

@ -35,7 +35,7 @@ namespace ix
int port, int port,
int timeoutSecs); int timeoutSecs);
WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate); WebSocketInitResult serverHandshake(int timeoutSecs);
private: private:
std::string genRandomString(const int len); std::string genRandomString(const int len);

View File

@ -129,8 +129,7 @@ namespace ix
_clients.insert(webSocket); _clients.insert(webSocket);
} }
auto status = webSocket->connectToSocket( auto status = webSocket->connectToSocket(std::move(socket), _handshakeTimeoutSecs);
std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate);
if (status.success) if (status.success)
{ {
// Process incoming messages and execute callbacks // Process incoming messages and execute callbacks
@ -176,39 +175,40 @@ namespace ix
// //
void WebSocketServer::makeBroadcastServer() void WebSocketServer::makeBroadcastServer()
{ {
setOnClientMessageCallback([this](std::shared_ptr<ConnectionState> connectionState, setOnClientMessageCallback(
WebSocket& webSocket, [this](std::shared_ptr<ConnectionState> connectionState,
const WebSocketMessagePtr& msg) { WebSocket& webSocket,
auto remoteIp = connectionState->getRemoteIp(); const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Message) auto remoteIp = connectionState->getRemoteIp();
{ if (msg->type == ix::WebSocketMessageType::Message)
for (auto&& client : getClients())
{ {
if (client.get() != &webSocket) for (auto&& client : getClients())
{ {
client->send(msg->str, msg->binary); if (client.get() != &webSocket)
// Make sure the OS send buffer is flushed before moving on
do
{ {
std::chrono::duration<double, std::milli> duration(500); client->send(msg->str, msg->binary);
std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0); // Make sure the OS send buffer is flushed before moving on
do
{
size_t bufferedAmount = client->bufferedAmount();
std::chrono::duration<double, std::milli> duration(500);
std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0);
}
} }
} }
} });
});
} }
bool WebSocketServer::listenAndStart() int WebSocketServer::listenAndStart()
{ {
auto res = listen(); auto res = listen();
if (!res.first) if (!res.first)
{ {
return false; return 1;
} }
start(); start();
return true;
} }
} // namespace ix } // namespace ix

View File

@ -48,7 +48,7 @@ namespace ix
std::set<std::shared_ptr<WebSocket>> getClients(); std::set<std::shared_ptr<WebSocket>> getClients();
void makeBroadcastServer(); void makeBroadcastServer();
bool listenAndStart(); int listenAndStart();
const static int kDefaultHandShakeTimeoutSecs; const static int kDefaultHandShakeTimeoutSecs;

View File

@ -169,8 +169,7 @@ namespace ix
// Server // Server
WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket,
int timeoutSecs, int timeoutSecs)
bool enablePerMessageDeflate)
{ {
std::lock_guard<std::mutex> lock(_socketMutex); std::lock_guard<std::mutex> lock(_socketMutex);
@ -187,7 +186,7 @@ namespace ix
_perMessageDeflateOptions, _perMessageDeflateOptions,
_enablePerMessageDeflate); _enablePerMessageDeflate);
auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate); auto result = webSocketHandshake.serverHandshake(timeoutSecs);
if (result.success) if (result.success)
{ {
setReadyState(ReadyState::OPEN); setReadyState(ReadyState::OPEN);

View File

@ -83,9 +83,7 @@ namespace ix
int timeoutSecs); int timeoutSecs);
// Server // Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs);
int timeoutSecs,
bool enablePerMessageDeflate);
PollResult poll(); PollResult poll();
WebSocketSendInfo sendBinary(const std::string& message, WebSocketSendInfo sendBinary(const std::string& message,

View File

@ -6,4 +6,4 @@
#pragma once #pragma once
#define IX_WEBSOCKET_VERSION "11.2.6" #define IX_WEBSOCKET_VERSION "11.0.8"

View File

@ -9,13 +9,10 @@
* $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install * $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install
* $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation * $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
* $ ./a.out * $ ./a.out
*
* Or use cmake -DBUILD_DEMO=ON option for other platform
*/ */
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXUserAgent.h>
#include <iostream> #include <iostream>
int main() int main()
@ -26,12 +23,9 @@ int main()
// Our websocket object // Our websocket object
ix::WebSocket webSocket; 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"); std::string url("wss://echo.websocket.org");
webSocket.setUrl(url); webSocket.setUrl(url);
std::cout << ix::userAgent() << std::endl;
std::cout << "Connecting to " << url << "..." << std::endl; std::cout << "Connecting to " << url << "..." << std::endl;
// Setup a callback to be fired (in a background thread, watch out for race conditions !) // Setup a callback to be fired (in a background thread, watch out for race conditions !)
@ -41,18 +35,10 @@ int main()
if (msg->type == ix::WebSocketMessageType::Message) if (msg->type == ix::WebSocketMessageType::Message)
{ {
std::cout << "received message: " << msg->str << std::endl; std::cout << "received message: " << msg->str << std::endl;
std::cout << "> " << std::flush;
} }
else if (msg->type == ix::WebSocketMessageType::Open) else if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::cout << "Connection established" << std::endl; 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;
} }
} }
); );
@ -63,16 +49,13 @@ int main()
// Send a message to the server (default to TEXT mode) // Send a message to the server (default to TEXT mode)
webSocket.send("hello world"); webSocket.send("hello world");
// Display a prompt while (true)
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::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
webSocket.send(text);
} }
return 0; return 0;

View File

@ -113,7 +113,7 @@ test_server:
test: test:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..)
(cd build ; ninja) (cd build ; ninja)
(cd build ; ninja -v test) (cd build ; ninja test)
test_asan: test_asan:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer") mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer")

View File

@ -33,7 +33,11 @@ TEST_CASE("dns", "[net]")
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
std::string errMsg; std::string errMsg;
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
return false;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }
@ -44,7 +48,11 @@ TEST_CASE("dns", "[net]")
std::string errMsg; std::string errMsg;
// The callback returning true means we are requesting cancellation // The callback returning true means we are requesting cancellation
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
return true;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }

View File

@ -139,9 +139,8 @@ namespace ix
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.reserve((size_t) size); memblock.resize((size_t) size);
memblock.insert( file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
return memblock; return memblock;
} }

View File

@ -1639,10 +1639,7 @@ bool enableRawMode(int fd) {
/* Init windows console handles only once */ /* Init windows console handles only once */
hOut = GetStdHandle(STD_OUTPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut==INVALID_HANDLE_VALUE) { if (hOut==INVALID_HANDLE_VALUE) goto fatal;
errno = ENOTTY;
return false;
}
} }
DWORD consolemodeOut; DWORD consolemodeOut;

View File

@ -55,17 +55,16 @@ namespace
std::pair<bool, std::vector<uint8_t>> load(const std::string& path) std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
{ {
std::vector<uint8_t> memblock; std::vector<uint8_t> memblock;
std::ifstream file(path);
std::ifstream file(path);
if (!file.is_open()) return std::make_pair(false, memblock); if (!file.is_open()) return std::make_pair(false, memblock);
file.seekg(0, file.end); file.seekg(0, file.end);
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.reserve((size_t) size); memblock.resize((size_t) size);
memblock.insert( file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
return std::make_pair(true, memblock); return std::make_pair(true, memblock);
} }
@ -87,9 +86,9 @@ namespace
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.reserve((size_t) size); memblock.resize(size);
memblock.insert(
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>()); file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
std::string bytes(memblock.begin(), memblock.end()); std::string bytes(memblock.begin(), memblock.end());
return bytes; return bytes;
@ -906,7 +905,7 @@ namespace ix
// code which display correct results // code which display correct results
char str[INET_ADDRSTRLEN]; char str[INET_ADDRSTRLEN];
ix::inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN);
spdlog::info("host: {} ip: {}", hostname, str); spdlog::info("host: {} ip: {}", hostname, str);
@ -1425,18 +1424,10 @@ namespace ix
filename = output; filename = output;
} }
if (filename.empty()) spdlog::info("Writing to disk: {}", filename);
{ std::ofstream out(filename);
spdlog::error("Cannot save content to disk: No output file supplied, and not " out.write((char*) &response->body.front(), response->body.size());
"filename could be extracted from the url {}", out.close();
url);
}
else
{
spdlog::info("Writing to disk: {}", filename);
std::ofstream out(filename);
out << response->body;
}
} }
else else
{ {
@ -1893,8 +1884,7 @@ namespace ix
spdlog::info("ws_receive: Writing to disk: {}", filenameTmp); spdlog::info("ws_receive: Writing to disk: {}", filenameTmp);
std::ofstream out(filenameTmp); std::ofstream out(filenameTmp);
std::string contentAsString(content.begin(), content.end()); out.write((char*) &content.front(), content.size());
out << contentAsString;
out.close(); out.close();
spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename); spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename);
@ -2082,6 +2072,23 @@ namespace ix
_condition.wait(lock); _condition.wait(lock);
} }
std::vector<uint8_t> load(const std::string& path)
{
std::vector<uint8_t> memblock;
std::ifstream file(path);
if (!file.is_open()) return 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 memblock;
}
void WebSocketSender::start() void WebSocketSender::start()
{ {
_webSocket.setUrl(_url); _webSocket.setUrl(_url);
@ -2175,8 +2182,7 @@ namespace ix
std::vector<uint8_t> content; std::vector<uint8_t> content;
{ {
Bench bench("ws_send: load file from disk"); Bench bench("ws_send: load file from disk");
auto res = load(filename); content = load(filename);
content = res.second;
} }
_id = uuid4(); _id = uuid4();
@ -2372,9 +2378,9 @@ namespace ix
else else
{ {
std::string readyStateString = std::string readyStateString =
readyState == ReadyState::Connecting ? "Connecting" readyState == ReadyState::Connecting
: readyState == ReadyState::Closing ? "Closing" ? "Connecting"
: "Closed"; : readyState == ReadyState::Closing ? "Closing" : "Closed";
size_t bufferedAmount = client->bufferedAmount(); size_t bufferedAmount = client->bufferedAmount();
spdlog::info( spdlog::info(
@ -2487,7 +2493,7 @@ int main(int argc, char** argv)
int delayMs = -1; int delayMs = -1;
int count = 1; int count = 1;
int msgCount = 1000 * 1000; int msgCount = 1000 * 1000;
uint32_t maxWaitBetweenReconnectionRetries = 10 * 1000; // 10 seconds uint32_t maxWaitBetweenReconnectionRetries;
int pingIntervalSecs = 30; int pingIntervalSecs = 30;
int runCount = 1; int runCount = 1;
@ -2768,14 +2774,8 @@ int main(int argc, char** argv)
ix::WebSocketServer server(port, hostname); ix::WebSocketServer server(port, hostname);
server.setTLSOptions(tlsOptions); server.setTLSOptions(tlsOptions);
server.makeBroadcastServer(); server.makeBroadcastServer();
if (!server.listenAndStart()) server.listenAndStart();
{ server.wait();
spdlog::error("Error while starting the server");
}
else
{
server.wait();
}
} }
else if (app.got_subcommand("send")) else if (app.got_subcommand("send"))
{ {