aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/httpserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenhttp/httpserver.cpp')
-rw-r--r--src/zenhttp/httpserver.cpp111
1 files changed, 104 insertions, 7 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp
index a46c5b851..e05c9815f 100644
--- a/src/zenhttp/httpserver.cpp
+++ b/src/zenhttp/httpserver.cpp
@@ -329,6 +329,10 @@ ReasonStringForHttpResultCode(int HttpCode)
return "Continue"sv;
case 101:
return "Switching Protocols"sv;
+ case 102:
+ return "Processing"sv;
+ case 103:
+ return "Early Hints"sv;
// 2xx Success
@@ -338,12 +342,20 @@ ReasonStringForHttpResultCode(int HttpCode)
return "Created"sv;
case 202:
return "Accepted"sv;
+ case 203:
+ return "Non-Authoritative Information"sv;
case 204:
return "No Content"sv;
case 205:
return "Reset Content"sv;
case 206:
return "Partial Content"sv;
+ case 207:
+ return "Multi-Status"sv;
+ case 208:
+ return "Already Reported"sv;
+ case 226:
+ return "IM Used"sv;
// 3xx Redirection
@@ -424,6 +436,8 @@ ReasonStringForHttpResultCode(int HttpCode)
return "Too Many Requests"sv;
case 431:
return "Request Header Fields Too Large"sv;
+ case 451:
+ return "Unavailable For Legal Reasons"sv;
// 5xx Server errors
@@ -798,7 +812,18 @@ HttpRequestRouter::HandleRequest(zen::HttpServerRequest& Request)
const HttpVerb Verb = Request.RequestVerb();
- std::string_view Uri = Request.RelativeUri();
+ std::string_view Uri = Request.RelativeUri();
+
+ // Strip the separator slash left over after the service prefix is removed.
+ // When a service has BaseUri "/foo", the prefix length is set to len("/foo") = 4.
+ // Stripping 4 chars from "/foo/bar" yields "/bar" — the path separator becomes
+ // the first character of the relative URI. Remove it so patterns like "bar" or
+ // "{id}" match without needing to account for the leading slash.
+ if (!Uri.empty() && Uri.front() == '/')
+ {
+ Uri.remove_prefix(1);
+ }
+
HttpRouterRequest RouterRequest(Request);
for (const MatcherEndpoint& Handler : m_MatcherEndpoints)
@@ -974,6 +999,12 @@ HttpServer::SetHttpRequestFilter(IHttpRequestFilter* RequestFilter)
OnSetHttpRequestFilter(RequestFilter);
}
+void
+HttpServer::HandleStatsRequest(HttpServerRequest& Request)
+{
+ Request.WriteResponse(HttpResponseCode::OK, CollectStats());
+}
+
CbObject
HttpServer::CollectStats()
{
@@ -1004,12 +1035,6 @@ HttpServer::CollectStats()
return Cbo.Save();
}
-void
-HttpServer::HandleStatsRequest(HttpServerRequest& Request)
-{
- Request.WriteResponse(HttpResponseCode::OK, CollectStats());
-}
-
//////////////////////////////////////////////////////////////////////////
HttpRpcHandler::HttpRpcHandler()
@@ -1446,6 +1471,78 @@ TEST_CASE("http.common")
}
}
+ SUBCASE("router-leading-slash")
+ {
+ // Verify that HandleRequest strips the leading slash that server implementations
+ // leave in RelativeUri() when the service base URI has no trailing slash.
+ // e.g. BaseUri "/stats" + prefix-strip of "/stats/foo" yields "/foo", not "foo".
+
+ bool HandledLiteral = false;
+ bool HandledPattern = false;
+ bool HandledTwoSeg = false;
+ std::vector<std::string> Captures;
+ auto Reset = [&] {
+ HandledLiteral = HandledPattern = HandledTwoSeg = false;
+ Captures.clear();
+ };
+
+ TestHttpService Service;
+ HttpRequestRouter r;
+
+ r.AddMatcher("seg", [](std::string_view In) -> bool { return !In.empty() && In.find('/') == std::string_view::npos; });
+
+ r.RegisterRoute(
+ "activity_counters",
+ [&](auto& /*Req*/) { HandledLiteral = true; },
+ HttpVerb::kGet);
+
+ r.RegisterRoute(
+ "{seg}",
+ [&](auto& Req) {
+ HandledPattern = true;
+ Captures = {std::string(Req.GetCapture(1))};
+ },
+ HttpVerb::kGet);
+
+ r.RegisterRoute(
+ "prefix/{seg}",
+ [&](auto& Req) {
+ HandledTwoSeg = true;
+ Captures = {std::string(Req.GetCapture(1))};
+ },
+ HttpVerb::kGet);
+
+ // Single-segment literal with leading slash — simulates real server RelativeUri
+ {
+ Reset();
+ TestHttpServerRequest req{Service, "/activity_counters"sv};
+ r.HandleRequest(req);
+ CHECK(HandledLiteral);
+ CHECK(!HandledPattern);
+ }
+
+ // Single-segment pattern with leading slash
+ {
+ Reset();
+ TestHttpServerRequest req{Service, "/hello"sv};
+ r.HandleRequest(req);
+ CHECK(!HandledLiteral);
+ CHECK(HandledPattern);
+ REQUIRE_EQ(Captures.size(), 1);
+ CHECK_EQ(Captures[0], "hello"sv);
+ }
+
+ // Two-segment route with leading slash — first literal segment
+ {
+ Reset();
+ TestHttpServerRequest req{Service, "/prefix/world"sv};
+ r.HandleRequest(req);
+ CHECK(HandledTwoSeg);
+ REQUIRE_EQ(Captures.size(), 1);
+ CHECK_EQ(Captures[0], "world"sv);
+ }
+ }
+
SUBCASE("content-type")
{
for (uint8_t i = 0; i < uint8_t(HttpContentType::kCOUNT); ++i)