diff --git a/include/elio/http/websocket_server.hpp b/include/elio/http/websocket_server.hpp index 146e81ca..fedb5d90 100644 --- a/include/elio/http/websocket_server.hpp +++ b/include/elio/http/websocket_server.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -479,6 +480,10 @@ class ws_router : public router { seg.value.assign(comp.data() + 1, comp.size() - 1); route.param_names.push_back(seg.value); } else if (comp == "*") { + if (i != pattern.size()) { + throw std::invalid_argument( + "WebSocket route wildcard must be the final path segment"); + } seg.kind = segment_kind::wildcard; } else { seg.kind = segment_kind::literal; diff --git a/tests/unit/test_websocket.cpp b/tests/unit/test_websocket.cpp index a45a640e..85ddb63f 100644 --- a/tests/unit/test_websocket.cpp +++ b/tests/unit/test_websocket.cpp @@ -944,3 +944,22 @@ TEST_CASE("WebSocket handshake building", "[websocket][handshake]") { REQUIRE(response.find("Connection: close\r\n") != std::string::npos); } } + +TEST_CASE("WebSocket route wildcard validation", "[websocket][router]") { + auto handler = [](ws_connection&) -> elio::coro::task { + co_return; + }; + + ws_router router; + REQUIRE_NOTHROW(router.websocket("/chat/*", handler)); + REQUIRE(router.find_ws_route("/chat/room") != nullptr); + REQUIRE(router.find_ws_route("/chat/") != nullptr); + REQUIRE(router.find_ws_route("/chat") == nullptr); + + REQUIRE_THROWS_AS(router.websocket("/chat/*/admin", handler), + std::invalid_argument); + REQUIRE_THROWS_AS(router.websocket("*/tail", handler), + std::invalid_argument); + REQUIRE_THROWS_AS(router.websocket("/chat/*/", handler), + std::invalid_argument); +}