diff --git a/src/web/impl/HttpBase.h b/src/web/impl/HttpBase.h index 2057b22b..ddfc049a 100644 --- a/src/web/impl/HttpBase.h +++ b/src/web/impl/HttpBase.h @@ -187,11 +187,16 @@ public: if (boost::beast::websocket::is_upgrade(req_)) { - // Disable the timeout. The websocket::stream uses its own timeout settings. - boost::beast::get_lowest_layer(derived().stream()).expires_never(); + if (dosGuard_.get().isOk(this->clientIp)) + { + // Disable the timeout. The websocket::stream uses its own timeout settings. + boost::beast::get_lowest_layer(derived().stream()).expires_never(); - upgraded = true; - return derived().upgrade(); + upgraded = true; + return derived().upgrade(); + } + + return sender_(httpResponse(http::status::too_many_requests, "text/html", "Too many requests")); } if (req_.method() != http::verb::post) diff --git a/src/web/impl/WsBase.h b/src/web/impl/WsBase.h index aee77c3a..6b5b694b 100644 --- a/src/web/impl/WsBase.h +++ b/src/web/impl/WsBase.h @@ -60,10 +60,10 @@ protected: void wsFail(boost::beast::error_code ec, char const* what) { + LOG(perfLog_.error()) << tag() << ": " << what << ": " << ec.message(); if (!ec_ && ec != boost::asio::error::operation_aborted) { ec_ = ec; - LOG(perfLog_.error()) << tag() << ": " << what << ": " << ec.message(); boost::beast::get_lowest_layer(derived().ws()).socket().close(ec); (*handler_)(ec, derived().shared_from_this()); } diff --git a/unittests/web/ServerTests.cpp b/unittests/web/ServerTests.cpp index 7f169b76..855eac74 100644 --- a/unittests/web/ServerTests.cpp +++ b/unittests/web/ServerTests.cpp @@ -361,6 +361,31 @@ TEST_F(WebServerTest, WsPayloadOverload) R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"); } +TEST_F(WebServerTest, WsTooManyConnection) +{ + auto e = std::make_shared(); + auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e); + // max connection is 2, exception should happen when the third connection is made + WebSocketSyncClient wsClient1; + wsClient1.connect("localhost", "8888"); + WebSocketSyncClient wsClient2; + wsClient2.connect("localhost", "8888"); + bool exceptionThrown = false; + try + { + WebSocketSyncClient wsClient3; + wsClient3.connect("localhost", "8888"); + } + catch (boost::system::system_error const& ex) + { + exceptionThrown = true; + EXPECT_EQ(ex.code(), boost::beast::websocket::error::upgrade_declined); + } + wsClient1.disconnect(); + wsClient2.disconnect(); + EXPECT_TRUE(exceptionThrown); +} + static auto constexpr JSONServerConfigWithAdminPassword = R"JSON( { "server":{