Compare commits

..

7 Commits

Author SHA1 Message Date
Benjamin Sergeant
e31b913280 docker debugging 2021-02-15 11:16:55 -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
14 changed files with 98 additions and 136 deletions

View File

@@ -2,7 +2,7 @@ name: linux
on: on:
push: push:
paths-ignore: paths-ignore:
- './**' - 'docs/**'
jobs: jobs:
linux: linux:

View File

@@ -2,7 +2,7 @@ name: linux_asan
on: on:
push: push:
paths-ignore: paths-ignore:
- './**' - 'docs/**'
jobs: jobs:
linux: linux:

View File

@@ -2,7 +2,7 @@ name: mac_tsan_mbedtls
on: on:
push: push:
paths-ignore: paths-ignore:
- './**' - 'docs/**'
jobs: jobs:
mac_tsan_mbedtls: mac_tsan_mbedtls:

View File

@@ -2,7 +2,7 @@ name: mac_tsan_openssl
on: on:
push: push:
paths-ignore: paths-ignore:
- './**' - 'docs/**'
jobs: jobs:
mac_tsan_openssl: mac_tsan_openssl:

View File

@@ -2,7 +2,7 @@ name: mac_tsan_sectransport
on: on:
push: push:
paths-ignore: paths-ignore:
- './**' - 'docs/**'
jobs: jobs:
mac_tsan_sectransport: mac_tsan_sectransport:

View File

@@ -2,7 +2,7 @@ name: uwp
on: on:
push: push:
paths-ignore: paths-ignore:
- './**' - 'docs/**'
jobs: jobs:
uwp: uwp:

View File

@@ -17,7 +17,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
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")

View File

@@ -77,6 +77,8 @@ IXWebSocket is actively being developed, check out the [changelog](https://machi
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. 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 ## Users
If your company or project is using this library, feel free to open an issue or PR to amend this list. If your company or project is using this library, feel free to open an issue or PR to amend this list.
@@ -113,6 +115,10 @@ To check the performance of a websocket library, you can look at the [autoroute]
| UWP | Disabled | None | [![Build2][6]][0] | | UWP | Disabled | None | [![Build2][6]][0] |
| Linux | OpenSSL | Address Sanitizer | [![Build2][7]][0] | | Linux | OpenSSL | Address Sanitizer | [![Build2][7]][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 [0]: https://github.com/machinezone/IXWebSocket
[1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg [1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg
[2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg [2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg

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

@@ -97,9 +97,10 @@ namespace ix
} }
else if (_onClientMessageCallback) else if (_onClientMessageCallback)
{ {
WebSocket* webSocketRawPtr = webSocket.get();
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[this, &ws = *webSocket.get(), connectionState](const WebSocketMessagePtr& msg) { [this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, ws, msg); _onClientMessageCallback(connectionState, *webSocketRawPtr, msg);
}); });
} }
else else
@@ -168,4 +169,46 @@ namespace ix
std::lock_guard<std::mutex> lock(_clientsMutex); std::lock_guard<std::mutex> lock(_clientsMutex);
return _clients.size(); return _clients.size();
} }
//
// Classic servers
//
void WebSocketServer::makeBroadcastServer()
{
setOnClientMessageCallback(
[this](std::shared_ptr<ConnectionState> connectionState,
WebSocket& webSocket,
const WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : getClients())
{
if (client.get() != &webSocket)
{
client->send(msg->str, msg->binary);
// 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);
}
}
}
});
}
int WebSocketServer::listenAndStart()
{
auto res = listen();
if (!res.first)
{
return 1;
}
start();
}
} // namespace ix } // namespace ix

View File

@@ -47,6 +47,9 @@ namespace ix
// Get all the connected clients // Get all the connected clients
std::set<std::shared_ptr<WebSocket>> getClients(); std::set<std::shared_ptr<WebSocket>> getClients();
void makeBroadcastServer();
int listenAndStart();
const static int kDefaultHandShakeTimeoutSecs; const static int kDefaultHandShakeTimeoutSecs;
private: private:

View File

@@ -29,6 +29,7 @@ namespace ix
// Comparison should be case insensitive // Comparison should be case insensitive
REQUIRE(httpHeaders["Foo"] == "foo"); REQUIRE(httpHeaders["Foo"] == "foo");
REQUIRE(httpHeaders["Foo"] != "bar");
} }
SECTION("2") SECTION("2")
@@ -39,7 +40,7 @@ namespace ix
headers["Upgrade"] = "webSocket"; headers["Upgrade"] = "webSocket";
REQUIRE(CaseInsensitiveLess::cmp(headers["upgrade"], "WebSocket") == 0); REQUIRE(!CaseInsensitiveLess::cmp(headers["upGRADE"], "webSocket"));
} }
} }

102
ws/ws.cpp
View File

@@ -439,93 +439,6 @@ namespace ix
return generateReport(url) ? 0 : 1; return generateReport(url) ? 0 : 1;
} }
//
// broadcast server
//
int ws_broadcast_server_main(int port,
const std::string& hostname,
const ix::SocketTLSOptions& tlsOptions)
{
spdlog::info("Listening on {}:{}", hostname, port);
ix::WebSocketServer server(port, hostname);
server.setTLSOptions(tlsOptions);
server.setOnClientMessageCallback(
[&server](std::shared_ptr<ConnectionState> connectionState,
WebSocket& webSocket,
const WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
spdlog::info("New connection");
spdlog::info("remote ip: {}", remoteIp);
spdlog::info("id: {}", connectionState->getId());
spdlog::info("Uri: {}", msg->openInfo.uri);
spdlog::info("Headers:");
for (auto it : msg->openInfo.headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
spdlog::info("Closed connection: code {} reason {}",
msg->closeInfo.code,
msg->closeInfo.reason);
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Connection 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;
spdlog::info(ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Fragment)
{
spdlog::info("Received message fragment");
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
spdlog::info("Received {} bytes", msg->wireSize);
for (auto&& client : server.getClients())
{
if (client.get() != &webSocket)
{
client->send(msg->str, msg->binary, [](int current, int total) -> bool {
spdlog::info("Step {} out of {}", current, total);
return true;
});
do
{
size_t bufferedAmount = client->bufferedAmount();
spdlog::info("{} bytes left to be sent", bufferedAmount);
std::chrono::duration<double, std::milli> duration(500);
std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0);
}
}
}
});
auto res = server.listen();
if (!res.first)
{
spdlog::info(res.second);
return 1;
}
server.start();
server.wait();
return 0;
}
/* /*
* ws_chat.cpp * ws_chat.cpp
* Author: Benjamin Sergeant * Author: Benjamin Sergeant
@@ -988,6 +901,9 @@ namespace ix
auto addr = res->ai_addr; auto addr = res->ai_addr;
// FIXME: this display weird addresses / we could steal libuv inet.c
// code which display correct results
char str[INET_ADDRSTRLEN]; char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN);
@@ -2853,9 +2769,13 @@ int main(int argc, char** argv)
ret = ix::ws_push_server( ret = ix::ws_push_server(
port, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong, sendMsg); port, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong, sendMsg);
} }
else if (app.got_subcommand("transfer")) else if (app.got_subcommand("transfer") || app.got_subcommand("broadcast_server"))
{ {
ret = ix::ws_transfer_main(port, hostname, tlsOptions); ix::WebSocketServer server(port, hostname);
server.setTLSOptions(tlsOptions);
server.makeBroadcastServer();
server.listenAndStart();
server.wait();
} }
else if (app.got_subcommand("send")) else if (app.got_subcommand("send"))
{ {
@@ -2870,10 +2790,6 @@ int main(int argc, char** argv)
{ {
ret = ix::ws_chat_main(url, user); ret = ix::ws_chat_main(url, user);
} }
else if (app.got_subcommand("broadcast_server"))
{
ret = ix::ws_broadcast_server_main(port, hostname, tlsOptions);
}
else if (app.got_subcommand("ping")) else if (app.got_subcommand("ping"))
{ {
ret = ix::ws_ping_pong_main(url, tlsOptions); ret = ix::ws_ping_pong_main(url, tlsOptions);