diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c070c2a2..8d87f0fb 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,10 @@ All changes to this project will be documented in this file. +## [10.1.6] - 2020-08-06 + +(websocket server) Handle programmer error when the server callback is not registered properly (fix #227) + ## [10.1.5] - 2020-08-02 (ws) Add a new ws sub-command, push_server. This command runs a server which sends many messages in a loop to a websocket client. We can receive above 200,000 messages per second (cf #235). diff --git a/ixwebsocket/IXWebSocket.cpp b/ixwebsocket/IXWebSocket.cpp index 94c3ecb1..c3c42135 100644 --- a/ixwebsocket/IXWebSocket.cpp +++ b/ixwebsocket/IXWebSocket.cpp @@ -405,6 +405,11 @@ namespace ix _onMessageCallback = callback; } + bool WebSocket::isOnMessageCallbackRegistered() const + { + return _onMessageCallback != nullptr; + } + void WebSocket::setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback) { _onTrafficTrackerCallback = callback; diff --git a/ixwebsocket/IXWebSocket.h b/ixwebsocket/IXWebSocket.h index 0c288cd5..5c47d303 100644 --- a/ixwebsocket/IXWebSocket.h +++ b/ixwebsocket/IXWebSocket.h @@ -84,6 +84,7 @@ namespace ix const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage); void setOnMessageCallback(const OnMessageCallback& callback); + bool isOnMessageCallbackRegistered() const; static void setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback); static void resetTrafficTrackerCallback(); diff --git a/ixwebsocket/IXWebSocketServer.cpp b/ixwebsocket/IXWebSocketServer.cpp index 4e9746a1..16db25bc 100644 --- a/ixwebsocket/IXWebSocketServer.cpp +++ b/ixwebsocket/IXWebSocketServer.cpp @@ -86,6 +86,15 @@ namespace ix if (_onConnectionCallback) { _onConnectionCallback(webSocket, connectionState, std::move(connectionInfo)); + + if (!webSocket->isOnMessageCallbackRegistered()) + { + logError("WebSocketServer Application developer error: Server callback improperly " + "registerered."); + logError("Missing call to setOnMessageCallback inside setOnConnectionCallback."); + connectionState->setTerminated(); + return; + } } else if (_onClientMessageCallback) { diff --git a/test/compatibility/cpp/libwebsockets/devnull_client.cpp b/test/compatibility/cpp/libwebsockets/devnull_client.cpp index 9d651541..60412544 100644 --- a/test/compatibility/cpp/libwebsockets/devnull_client.cpp +++ b/test/compatibility/cpp/libwebsockets/devnull_client.cpp @@ -33,157 +33,139 @@ * messages received: 200825 per second 1677933 total * messages received: 183542 per second 1861475 total * ^C[2020/08/02 19:22:33:4450] U: Completed OK - * + * */ -#include -#include -#include - #include -#include #include +#include +#include +#include +#include static int interrupted; -static struct lws *client_wsi; +static struct lws* client_wsi; std::atomic receivedCount(0); -static int -callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) +static int callback_dumb_increment( + struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) { - switch (reason) { - + switch (reason) + { /* because we are protocols[0] ... */ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - client_wsi = NULL; - break; + lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char*) in : "(null)"); + client_wsi = NULL; + break; - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("%s: established\n", __func__); - break; + case LWS_CALLBACK_CLIENT_ESTABLISHED: lwsl_user("%s: established\n", __func__); break; - case LWS_CALLBACK_CLIENT_RECEIVE: - receivedCount++; - break; + case LWS_CALLBACK_CLIENT_RECEIVE: receivedCount++; break; - case LWS_CALLBACK_CLIENT_CLOSED: - client_wsi = NULL; - break; + case LWS_CALLBACK_CLIENT_CLOSED: client_wsi = NULL; break; - default: - break; - } + default: break; + } - return lws_callback_http_dummy(wsi, reason, user, in, len); + return lws_callback_http_dummy(wsi, reason, user, in, len); } -static const struct lws_protocols protocols[] = { +static const struct lws_protocols protocols[] = {{ + "dumb-increment-protocol", + callback_dumb_increment, + 0, + 0, + }, + {NULL, NULL, 0, 0}}; + +static void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char** argv) +{ + uint64_t receivedCountTotal(0); + uint64_t receivedCountPerSecs(0); + + auto timer = [&receivedCountTotal, &receivedCountPerSecs] { + while (!interrupted) { - "dumb-increment-protocol", - callback_dumb_increment, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; + std::cerr << "messages received: " << receivedCountPerSecs << " per second " + << receivedCountTotal << " total" << std::endl; -static void -sigint_handler(int sig) -{ - interrupted = 1; -} + receivedCountPerSecs = receivedCount - receivedCountTotal; + receivedCountTotal += receivedCountPerSecs; -int main(int argc, const char **argv) -{ - uint64_t receivedCountTotal(0); - uint64_t receivedCountPerSecs(0); - - auto timer = [&receivedCountTotal, &receivedCountPerSecs] { - while (!interrupted) - { - std::cerr << "messages received: " - << receivedCountPerSecs - << " per second " - << receivedCountTotal - << " total" - << std::endl; - - receivedCountPerSecs = receivedCount - receivedCountTotal; - receivedCountTotal += receivedCountPerSecs; - - auto duration = std::chrono::seconds(1); - std::this_thread::sleep_for(duration); - } - }; - - std::thread t1(timer); - - struct lws_context_creation_info info; - struct lws_client_connect_info i; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, lws - * must have been configured with -DCMAKE_BUILD_TYPE=DEBUG - * instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client rx [-d ] [--h2]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - info.timeout_secs = 10; - - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; + auto duration = std::chrono::seconds(1); + std::this_thread::sleep_for(duration); } + }; - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - i.port = 8008; - i.address = "127.0.0.1"; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.protocol = protocols[0].name; /* "dumb-increment-protocol" */ - i.pwsi = &client_wsi; + std::thread t1(timer); - if (lws_cmdline_option(argc, argv, "--h2")) - i.alpn = "h2"; + struct lws_context_creation_info info; + struct lws_client_connect_info i; + struct lws_context* context; + const char* p; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE + /* for LLL_ verbosity above NOTICE to be built into lws, lws + * must have been configured with -DCMAKE_BUILD_TYPE=DEBUG + * instead of =RELEASE */ + /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ + /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ + /* | LLL_DEBUG */; - lws_client_connect_via_info(&i); + signal(SIGINT, sigint_handler); + if ((p = lws_cmdline_option(argc, argv, "-d"))) logs = atoi(p); - while (n >= 0 && client_wsi && !interrupted) - n = lws_service(context, 0); + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal ws client rx [-d ] [--h2]\n"); - lws_context_destroy(context); + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ + info.protocols = protocols; + info.timeout_secs = 10; - lwsl_user("Completed %s\n", receivedCount > 10 ? "OK" : "Failed"); + /* + * since we know this lws context is only ever going to be used with + * one client wsis / fds / sockets at a time, let lws know it doesn't + * have to use the default allocations for fd tables up to ulimit -n. + * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we + * will use. + */ + info.fd_limit_per_thread = 1 + 1 + 1; - t1.join(); + context = lws_create_context(&info); + if (!context) + { + lwsl_err("lws init failed\n"); + return 1; + } - return receivedCount > 10; + memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ + i.context = context; + i.port = 8008; + i.address = "127.0.0.1"; + i.path = "/"; + i.host = i.address; + i.origin = i.address; + i.protocol = protocols[0].name; /* "dumb-increment-protocol" */ + i.pwsi = &client_wsi; + + if (lws_cmdline_option(argc, argv, "--h2")) i.alpn = "h2"; + + lws_client_connect_via_info(&i); + + while (n >= 0 && client_wsi && !interrupted) + n = lws_service(context, 0); + + lws_context_destroy(context); + + lwsl_user("Completed %s\n", receivedCount > 10 ? "OK" : "Failed"); + + t1.join(); + + return receivedCount > 10; }