diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 53b1c2fd..4751c5da 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All changes to this project will be documented in this file. +## [9.10.0] - 2020-07-23 + +(websocket server) add a new simpler API to handle client connections / that API does not trigger a memory leak while the previous one did + ## [9.9.3] - 2020-07-17 (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) diff --git a/docs/usage.md b/docs/usage.md index b4df5ddb..44335d61 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -246,6 +246,8 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries(); ## WebSocket server API +### Legacy api + ```cpp #include @@ -312,6 +314,74 @@ 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 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 + +... + +// 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.setOnClientMessageCallback(std::shared_ptr connectionState, + ConnectionInfo& connectionInfo, + WebSocket& webSocket, + const WebSocketMessagePtr& msg) +{ + // The ConnectionInfo object contains information about the connection, + // at this point only the client ip address and the port. + std::cout << "Remote ip: " << connectionInfo.remoteIp << 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 << 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. + webSocket.send(msg->str, msg->binary); + } +); + +auto res = server.listen(); +if (!res.first) +{ + // Error handling + return 1; +} + +// Run the server in the background. Server can be stoped by calling server.stop() +server.start(); + +// Block until server.stop() is called. +server.wait(); + +``` + ## HTTP client API ```cpp diff --git a/ixwebsocket/IXHttpServer.cpp b/ixwebsocket/IXHttpServer.cpp index af3aeb4c..4d544f71 100644 --- a/ixwebsocket/IXHttpServer.cpp +++ b/ixwebsocket/IXHttpServer.cpp @@ -125,9 +125,8 @@ namespace ix if (std::get<0>(ret)) { - auto response = _onConnectionCallback(std::get<2>(ret), - connectionState, - std::move(connectionInfo)); + auto response = + _onConnectionCallback(std::get<2>(ret), connectionState, std::move(connectionInfo)); if (!Http::sendResponse(response, socket)) { logError("Cannot send response"); @@ -203,10 +202,9 @@ namespace ix // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections // setOnConnectionCallback( - [this, - redirectUrl](HttpRequestPtr request, - std::shared_ptr /*connectionState*/, - std::unique_ptr connectionInfo) -> HttpResponsePtr { + [this, redirectUrl](HttpRequestPtr request, + std::shared_ptr /*connectionState*/, + std::unique_ptr connectionInfo) -> HttpResponsePtr { WebSocketHttpHeaders headers; headers["Server"] = userAgent(); diff --git a/ixwebsocket/IXSetThreadName.cpp b/ixwebsocket/IXSetThreadName.cpp index 9f656fc7..a1aa0272 100644 --- a/ixwebsocket/IXSetThreadName.cpp +++ b/ixwebsocket/IXSetThreadName.cpp @@ -7,17 +7,17 @@ // unix systems #if defined(__APPLE__) || defined(__linux__) || defined(BSD) -# include +#include #endif // freebsd needs this header as well #if defined(BSD) -# include +#include #endif // Windows #ifdef _WIN32 -# include +#include #endif namespace ix diff --git a/ixwebsocket/IXSocketServer.cpp b/ixwebsocket/IXSocketServer.cpp index abb3e735..6c33420b 100644 --- a/ixwebsocket/IXSocketServer.cpp +++ b/ixwebsocket/IXSocketServer.cpp @@ -379,10 +379,13 @@ namespace ix // Launch the handleConnection work asynchronously in its own thread. std::lock_guard lock(_connectionsThreadsMutex); - _connectionsThreads.push_back(std::make_pair( - connectionState, - std::thread( - &SocketServer::handleConnection, this, std::move(socket), connectionState, std::move(connectionInfo)))); + _connectionsThreads.push_back( + std::make_pair(connectionState, + std::thread(&SocketServer::handleConnection, + this, + std::move(socket), + connectionState, + std::move(connectionInfo)))); } } diff --git a/ixwebsocket/IXWebSocketServer.cpp b/ixwebsocket/IXWebSocketServer.cpp index 1a568850..16fbde63 100644 --- a/ixwebsocket/IXWebSocketServer.cpp +++ b/ixwebsocket/IXWebSocketServer.cpp @@ -71,6 +71,11 @@ namespace ix _onConnectionCallback = callback; } + void WebSocketServer::setOnClientMessageCallback(const OnClientMessageCallback& callback) + { + _onClientMessageCallback = callback; + } + void WebSocketServer::handleConnection(std::unique_ptr socket, std::shared_ptr connectionState, std::unique_ptr connectionInfo) @@ -78,7 +83,26 @@ namespace ix setThreadName("WebSocketServer::" + connectionState->getId()); auto webSocket = std::make_shared(); - _onConnectionCallback(webSocket, connectionState, std::move(connectionInfo)); + if (_onConnectionCallback) + { + _onConnectionCallback(webSocket, connectionState, std::move(connectionInfo)); + } + else if (_onClientMessageCallback) + { + webSocket->setOnMessageCallback( + [this, &ws = *webSocket.get(), connectionState, &ci = *connectionInfo.get()]( + const WebSocketMessagePtr& msg) { + _onClientMessageCallback(connectionState, ci, ws, msg); + }); + } + else + { + logError( + "WebSocketServer Application developer error: No server callback is registerered."); + logError("Missing call to setOnConnectionCallback or setOnClientMessageCallback."); + connectionState->setTerminated(); + return; + } webSocket->disableAutomaticReconnection(); diff --git a/ixwebsocket/IXWebSocketServer.h b/ixwebsocket/IXWebSocketServer.h index 32f4693f..77c6da84 100644 --- a/ixwebsocket/IXWebSocketServer.h +++ b/ixwebsocket/IXWebSocketServer.h @@ -23,9 +23,15 @@ namespace ix { public: using OnConnectionCallback = - std::function, std::shared_ptr, + std::function, + std::shared_ptr, std::unique_ptr connectionInfo)>; + using OnClientMessageCallback = std::function, + ConnectionInfo&, + WebSocket&, + const WebSocketMessagePtr&)>; + WebSocketServer(int port = SocketServer::kDefaultPort, const std::string& host = SocketServer::kDefaultHost, int backlog = SocketServer::kDefaultTcpBacklog, @@ -40,6 +46,7 @@ namespace ix void disablePerMessageDeflate(); void setOnConnectionCallback(const OnConnectionCallback& callback); + void setOnClientMessageCallback(const OnClientMessageCallback& callback); // Get all the connected clients std::set> getClients(); @@ -53,6 +60,7 @@ namespace ix bool _enablePerMessageDeflate; OnConnectionCallback _onConnectionCallback; + OnClientMessageCallback _onClientMessageCallback; std::mutex _clientsMutex; std::set> _clients; diff --git a/ixwebsocket/IXWebSocketVersion.h b/ixwebsocket/IXWebSocketVersion.h index e0dcb55f..13ebb6c4 100644 --- a/ixwebsocket/IXWebSocketVersion.h +++ b/ixwebsocket/IXWebSocketVersion.h @@ -6,4 +6,4 @@ #pragma once -#define IX_WEBSOCKET_VERSION "9.9.3" +#define IX_WEBSOCKET_VERSION "9.10.0" diff --git a/test/IXTest.cpp b/test/IXTest.cpp index 405e74a7..bfd01941 100644 --- a/test/IXTest.cpp +++ b/test/IXTest.cpp @@ -88,34 +88,34 @@ namespace ix std::shared_ptr connectionState, std::unique_ptr connectionInfo) { auto remoteIp = connectionInfo->remoteIp; - webSocket->setOnMessageCallback( - [webSocket, connectionState, remoteIp, &server](const ix::WebSocketMessagePtr& msg) { - if (msg->type == ix::WebSocketMessageType::Open) + webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server]( + const ix::WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Open) + { + TLogger() << "New connection"; + TLogger() << "Remote ip: " << remoteIp; + TLogger() << "Uri: " << msg->openInfo.uri; + TLogger() << "Headers:"; + for (auto it : msg->openInfo.headers) { - TLogger() << "New connection"; - TLogger() << "Remote ip: " << remoteIp; - TLogger() << "Uri: " << msg->openInfo.uri; - TLogger() << "Headers:"; - for (auto it : msg->openInfo.headers) + TLogger() << it.first << ": " << it.second; + } + } + else if (msg->type == ix::WebSocketMessageType::Close) + { + TLogger() << "Closed connection"; + } + else if (msg->type == ix::WebSocketMessageType::Message) + { + for (auto&& client : server.getClients()) + { + if (client != webSocket) { - TLogger() << it.first << ": " << it.second; + client->send(msg->str, msg->binary); } } - else if (msg->type == ix::WebSocketMessageType::Close) - { - TLogger() << "Closed connection"; - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - for (auto&& client : server.getClients()) - { - if (client != webSocket) - { - client->send(msg->str, msg->binary); - } - } - } - }); + } + }); }); auto res = server.listen(); diff --git a/test/IXWebSocketBroadcastTest.cpp b/test/IXWebSocketBroadcastTest.cpp index d9381586..d10bc6c7 100644 --- a/test/IXWebSocketBroadcastTest.cpp +++ b/test/IXWebSocketBroadcastTest.cpp @@ -189,44 +189,45 @@ namespace bool preferTLS = true; server.setTLSOptions(makeServerTLSOptions(preferTLS)); - server.setOnConnectionCallback([&server, &connectionId]( - std::shared_ptr webSocket, - std::shared_ptr connectionState, - std::unique_ptr connectionInfo) { - auto remoteIp = connectionInfo->remoteIp; - webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &connectionId, &server]( - const ix::WebSocketMessagePtr& msg) { - if (msg->type == ix::WebSocketMessageType::Open) - { - TLogger() << "New connection"; - connectionState->computeId(); - TLogger() << "remote ip: " << remoteIp; - TLogger() << "id: " << connectionState->getId(); - TLogger() << "Uri: " << msg->openInfo.uri; - TLogger() << "Headers:"; - for (auto it : msg->openInfo.headers) - { - TLogger() << it.first << ": " << it.second; - } - - connectionId = connectionState->getId(); - } - else if (msg->type == ix::WebSocketMessageType::Close) - { - TLogger() << "Closed connection"; - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - for (auto&& client : server.getClients()) - { - if (client != webSocket) + server.setOnConnectionCallback( + [&server, &connectionId](std::shared_ptr webSocket, + std::shared_ptr connectionState, + std::unique_ptr connectionInfo) { + auto remoteIp = connectionInfo->remoteIp; + webSocket->setOnMessageCallback( + [webSocket, connectionState, remoteIp, &connectionId, &server]( + const ix::WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Open) { - client->send(msg->str, msg->binary); + TLogger() << "New connection"; + connectionState->computeId(); + TLogger() << "remote ip: " << remoteIp; + TLogger() << "id: " << connectionState->getId(); + TLogger() << "Uri: " << msg->openInfo.uri; + TLogger() << "Headers:"; + for (auto it : msg->openInfo.headers) + { + TLogger() << it.first << ": " << it.second; + } + + connectionId = connectionState->getId(); } - } - } + else if (msg->type == ix::WebSocketMessageType::Close) + { + TLogger() << "Closed connection"; + } + else if (msg->type == ix::WebSocketMessageType::Message) + { + for (auto&& client : server.getClients()) + { + if (client != webSocket) + { + client->send(msg->str, msg->binary); + } + } + } + }); }); - }); auto res = server.listen(); if (!res.first) diff --git a/test/IXWebSocketChatTest.cpp b/test/IXWebSocketChatTest.cpp index caa3f5f7..9b806fe0 100644 --- a/test/IXWebSocketChatTest.cpp +++ b/test/IXWebSocketChatTest.cpp @@ -198,35 +198,35 @@ namespace std::unique_ptr connectionInfo) { auto remoteIp = connectionInfo->remoteIp; - webSocket->setOnMessageCallback( - [webSocket, connectionState, remoteIp, &server](const ix::WebSocketMessagePtr& msg) { - if (msg->type == ix::WebSocketMessageType::Open) + webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server]( + const ix::WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Open) + { + TLogger() << "New connection"; + TLogger() << "remote ip: " << remoteIp; + TLogger() << "id: " << connectionState->getId(); + TLogger() << "Uri: " << msg->openInfo.uri; + TLogger() << "Headers:"; + for (auto it : msg->openInfo.headers) { - TLogger() << "New connection"; - TLogger() << "remote ip: " << remoteIp; - TLogger() << "id: " << connectionState->getId(); - TLogger() << "Uri: " << msg->openInfo.uri; - TLogger() << "Headers:"; - for (auto it : msg->openInfo.headers) + TLogger() << it.first << ": " << it.second; + } + } + else if (msg->type == ix::WebSocketMessageType::Close) + { + log("Closed connection"); + } + else if (msg->type == ix::WebSocketMessageType::Message) + { + for (auto&& client : server.getClients()) + { + if (client != webSocket) { - TLogger() << it.first << ": " << it.second; + client->sendBinary(msg->str); } } - else if (msg->type == ix::WebSocketMessageType::Close) - { - log("Closed connection"); - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - for (auto&& client : server.getClients()) - { - if (client != webSocket) - { - client->sendBinary(msg->str); - } - } - } - }); + } + }); }); auto res = server.listen(); diff --git a/test/IXWebSocketLeakTest.cpp b/test/IXWebSocketLeakTest.cpp index c075c847..abbb4e2c 100644 --- a/test/IXWebSocketLeakTest.cpp +++ b/test/IXWebSocketLeakTest.cpp @@ -5,13 +5,11 @@ */ #include "IXTest.h" - #include "catch.hpp" -#include -#include - #include #include +#include +#include using namespace ix; @@ -69,8 +67,7 @@ namespace std::stringstream ss; log(std::string("Connecting to url: ") + url); - _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) - { + _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) { std::stringstream ss; if (msg->type == ix::WebSocketMessageType::Open) { @@ -118,34 +115,37 @@ TEST_CASE("Websocket leak test") int port = getFreePort(); WebSocketServer server(port); - server.setOnConnectionCallback([&webSocketPtr](std::shared_ptr webSocket, - std::shared_ptr connectionState, - std::unique_ptr connectionInfo) - { - // original ptr in WebSocketServer::handleConnection and the callback argument - REQUIRE(webSocket.use_count() == 2); - webSocket->setOnMessageCallback([&webSocketPtr, webSocket, connectionState](const ix::WebSocketMessagePtr& msg) - { - if (msg->type == ix::WebSocketMessageType::Open) - { - log(std::string("New connection id: ") + connectionState->getId()); - // original ptr in WebSocketServer::handleConnection, captured ptr of this callback, and ptr in WebSocketServer::_clients - REQUIRE(webSocket.use_count() == 3); - webSocketPtr = std::shared_ptr(webSocket); - REQUIRE(webSocket.use_count() == 4); - } - else if (msg->type == ix::WebSocketMessageType::Close) - { - log(std::string("Client closed connection id: ") + connectionState->getId()); - } - else - { - log(std::string(msg->str)); - } + server.setOnConnectionCallback( + [&webSocketPtr](std::shared_ptr webSocket, + std::shared_ptr connectionState, + std::unique_ptr connectionInfo) { + // original ptr in WebSocketServer::handleConnection and the callback argument + REQUIRE(webSocket.use_count() == 2); + webSocket->setOnMessageCallback([&webSocketPtr, webSocket, connectionState]( + const ix::WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Open) + { + log(std::string("New connection id: ") + connectionState->getId()); + // original ptr in WebSocketServer::handleConnection, captured ptr of + // this callback, and ptr in WebSocketServer::_clients + REQUIRE(webSocket.use_count() == 3); + webSocketPtr = std::shared_ptr(webSocket); + REQUIRE(webSocket.use_count() == 4); + } + else if (msg->type == ix::WebSocketMessageType::Close) + { + log(std::string("Client closed connection id: ") + + connectionState->getId()); + } + else + { + log(std::string(msg->str)); + } + }); + // original ptr in WebSocketServer::handleConnection, argument of this callback, + // and captured ptr in websocket callback + REQUIRE(webSocket.use_count() == 3); }); - // original ptr in WebSocketServer::handleConnection, argument of this callback, and captured ptr in websocket callback - REQUIRE(webSocket.use_count() == 3); - }); server.listen(); server.start(); @@ -169,7 +169,8 @@ TEST_CASE("Websocket leak test") ix::msleep(500); REQUIRE(server.getClients().size() == 0); - // websocket should only be referenced by webSocketPtr but is still used by the websocket callback + // websocket should only be referenced by webSocketPtr but is still used by the + // websocket callback REQUIRE(webSocketPtr.use_count() == 1); webSocketPtr->setOnMessageCallback(nullptr); // websocket should only be referenced by webSocketPtr diff --git a/test/IXWebSocketServerTest.cpp b/test/IXWebSocketServerTest.cpp index a375aa02..62fa1388 100644 --- a/test/IXWebSocketServerTest.cpp +++ b/test/IXWebSocketServerTest.cpp @@ -33,44 +33,45 @@ namespace ix }; server.setConnectionStateFactory(factory); - server.setOnConnectionCallback([&server, &connectionId]( - std::shared_ptr webSocket, - std::shared_ptr connectionState, - std::unique_ptr connectionInfo) { - auto remoteIp = connectionInfo->remoteIp; - webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &connectionId, &server]( - const ix::WebSocketMessagePtr& msg) { - if (msg->type == ix::WebSocketMessageType::Open) - { - TLogger() << "New connection"; - connectionState->computeId(); - TLogger() << "remote ip: " << remoteIp; - TLogger() << "id: " << connectionState->getId(); - TLogger() << "Uri: " << msg->openInfo.uri; - TLogger() << "Headers:"; - for (auto it : msg->openInfo.headers) - { - TLogger() << it.first << ": " << it.second; - } - - connectionId = connectionState->getId(); - } - else if (msg->type == ix::WebSocketMessageType::Close) - { - TLogger() << "Closed connection"; - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - for (auto&& client : server.getClients()) - { - if (client != webSocket) + server.setOnConnectionCallback( + [&server, &connectionId](std::shared_ptr webSocket, + std::shared_ptr connectionState, + std::unique_ptr connectionInfo) { + auto remoteIp = connectionInfo->remoteIp; + webSocket->setOnMessageCallback( + [webSocket, connectionState, remoteIp, &connectionId, &server]( + const ix::WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Open) { - client->send(msg->str, msg->binary); + TLogger() << "New connection"; + connectionState->computeId(); + TLogger() << "remote ip: " << remoteIp; + TLogger() << "id: " << connectionState->getId(); + TLogger() << "Uri: " << msg->openInfo.uri; + TLogger() << "Headers:"; + for (auto it : msg->openInfo.headers) + { + TLogger() << it.first << ": " << it.second; + } + + connectionId = connectionState->getId(); } - } - } + else if (msg->type == ix::WebSocketMessageType::Close) + { + TLogger() << "Closed connection"; + } + else if (msg->type == ix::WebSocketMessageType::Message) + { + for (auto&& client : server.getClients()) + { + if (client != webSocket) + { + client->send(msg->str, msg->binary); + } + } + } + }); }); - }); auto res = server.listen(); if (!res.first) diff --git a/test/IXWebSocketSubProtocolTest.cpp b/test/IXWebSocketSubProtocolTest.cpp index 40a4037a..d2bb0d03 100644 --- a/test/IXWebSocketSubProtocolTest.cpp +++ b/test/IXWebSocketSubProtocolTest.cpp @@ -21,37 +21,38 @@ bool startServer(ix::WebSocketServer& server, std::string& subProtocols) std::shared_ptr connectionState, std::unique_ptr connectionInfo) { auto remoteIp = connectionInfo->remoteIp; - webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server, &subProtocols]( - const ix::WebSocketMessagePtr& msg) { - if (msg->type == ix::WebSocketMessageType::Open) - { - TLogger() << "New connection"; - TLogger() << "remote ip: " << remoteIp; - TLogger() << "id: " << connectionState->getId(); - TLogger() << "Uri: " << msg->openInfo.uri; - TLogger() << "Headers:"; - for (auto it : msg->openInfo.headers) + webSocket->setOnMessageCallback( + [webSocket, connectionState, remoteIp, &server, &subProtocols]( + const ix::WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Open) { - TLogger() << it.first << ": " << it.second; - } - - subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"]; - } - else if (msg->type == ix::WebSocketMessageType::Close) - { - log("Closed connection"); - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - for (auto&& client : server.getClients()) - { - if (client != webSocket) + TLogger() << "New connection"; + TLogger() << "remote ip: " << remoteIp; + TLogger() << "id: " << connectionState->getId(); + TLogger() << "Uri: " << msg->openInfo.uri; + TLogger() << "Headers:"; + for (auto it : msg->openInfo.headers) { - client->sendBinary(msg->str); + TLogger() << it.first << ": " << it.second; + } + + subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"]; + } + else if (msg->type == ix::WebSocketMessageType::Close) + { + log("Closed connection"); + } + else if (msg->type == ix::WebSocketMessageType::Message) + { + for (auto&& client : server.getClients()) + { + if (client != webSocket) + { + client->sendBinary(msg->str); + } } } - } - }); + }); }); auto res = server.listen(); diff --git a/ws/ws.cpp b/ws/ws.cpp index 5d3dbd4b..67b7743c 100644 --- a/ws/ws.cpp +++ b/ws/ws.cpp @@ -484,7 +484,24 @@ int main(int argc, char** argv) cobraBotConfig.cobraConfig.socketTLSOptions = tlsOptions; int ret = 1; - if (app.got_subcommand("transfer")) + if (app.got_subcommand("connect")) + { + ret = ix::ws_connect_main(url, + headers, + disableAutomaticReconnection, + disablePerMessageDeflate, + binaryMode, + maxWaitBetweenReconnectionRetries, + tlsOptions, + subprotocol, + pingIntervalSecs); + } + else if (app.got_subcommand("echo_server")) + { + ret = ix::ws_echo_server_main( + port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong); + } + else if (app.got_subcommand("transfer")) { ret = ix::ws_transfer_main(port, hostname, tlsOptions); } @@ -497,27 +514,10 @@ int main(int argc, char** argv) bool enablePerMessageDeflate = false; ret = ix::ws_receive_main(url, enablePerMessageDeflate, delayMs, tlsOptions); } - else if (app.got_subcommand("connect")) - { - ret = ix::ws_connect_main(url, - headers, - disableAutomaticReconnection, - disablePerMessageDeflate, - binaryMode, - maxWaitBetweenReconnectionRetries, - tlsOptions, - subprotocol, - pingIntervalSecs); - } else if (app.got_subcommand("chat")) { ret = ix::ws_chat_main(url, user); } - else if (app.got_subcommand("echo_server")) - { - ret = ix::ws_echo_server_main( - port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong); - } else if (app.got_subcommand("broadcast_server")) { ret = ix::ws_broadcast_server_main(port, hostname, tlsOptions); diff --git a/ws/ws_echo_server.cpp b/ws/ws_echo_server.cpp index 68017cb0..8c336435 100644 --- a/ws/ws_echo_server.cpp +++ b/ws/ws_echo_server.cpp @@ -42,50 +42,48 @@ namespace ix server.disablePong(); } - server.setOnConnectionCallback( - [greetings](std::shared_ptr webSocket, - std::shared_ptr connectionState, - std::unique_ptr connectionInfo) { - auto remoteIp = connectionInfo->remoteIp; - webSocket->setOnMessageCallback( - [webSocket, connectionState, remoteIp, greetings](const WebSocketMessagePtr& msg) { - 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); - } + server.setOnClientMessageCallback( + [greetings](std::shared_ptr connectionState, + ConnectionInfo& connectionInfo, + WebSocket& webSocket, + const WebSocketMessagePtr& msg) { + auto remoteIp = connectionInfo.remoteIp; + if (msg->type == ix::WebSocketMessageType::Open) + { + spdlog::info("New connection"); + spdlog::info("remote ip: {}", remoteIp); + spdlog::info("id: {}", connectionState->getId()); + spdlog::info("Uri: {}", msg->openInfo.uri); + spdlog::info("Headers:"); + for (auto it : msg->openInfo.headers) + { + spdlog::info("{}: {}", it.first, it.second); + } - if (greetings) - { - webSocket->sendText("Welcome !"); - } - } - else if (msg->type == ix::WebSocketMessageType::Close) - { - spdlog::info("Closed connection: client id {} code {} reason {}", - connectionState->getId(), - msg->closeInfo.code, - msg->closeInfo.reason); - } - else if (msg->type == ix::WebSocketMessageType::Error) - { - spdlog::error("Connection error: {}", msg->errorInfo.reason); - spdlog::error("#retries: {}", msg->errorInfo.retries); - spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time); - spdlog::error("HTTP Status: {}", msg->errorInfo.http_status); - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - spdlog::info("Received {} bytes", msg->wireSize); - webSocket->send(msg->str, msg->binary); - } - }); + if (greetings) + { + webSocket.sendText("Welcome !"); + } + } + else if (msg->type == ix::WebSocketMessageType::Close) + { + spdlog::info("Closed connection: client id {} code {} reason {}", + connectionState->getId(), + msg->closeInfo.code, + msg->closeInfo.reason); + } + else if (msg->type == ix::WebSocketMessageType::Error) + { + spdlog::error("Connection error: {}", msg->errorInfo.reason); + spdlog::error("#retries: {}", msg->errorInfo.retries); + spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time); + spdlog::error("HTTP Status: {}", msg->errorInfo.http_status); + } + else if (msg->type == ix::WebSocketMessageType::Message) + { + spdlog::info("Received {} bytes", msg->wireSize); + webSocket.send(msg->str, msg->binary); + } }); auto res = server.listen();