From bc6ba6aee2761b6cff22895d30e0ba7b3af4fb6a Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Wed, 26 Nov 2025 14:05:30 +0100 Subject: [PATCH] Redirect to path+/ if path doesn't end with / If HTML document uses relative paths, it'll fail to load them otherwise. $ echo hello > index.html && mkdir test && echo world > test/index.html $ curl -I http://127.0.0.1:8848 HTTP/1.1 200 OK content-length: 13 content-type: text/html; charset=utf-8 server: drogon/1.9.11 date: Wed, 26 Nov 2025 13:09:28 GMT $ curl -I http://127.0.0.1:8848/test HTTP/1.1 301 Moved Permanently content-length: 0 content-type: text/html; charset=utf-8 server: drogon/1.9.11 location: /test/ date: Wed, 26 Nov 2025 13:09:33 GMT $ curl -I http://127.0.0.1:8848/test/ HTTP/1.1 200 OK content-length: 6 content-type: text/html; charset=utf-8 server: drogon/1.9.11 accept-range: bytes expires: Thu, 01 Jan 1970 00:00:00 GMT last-modified: Wed, 26 Nov 2025 13:08:38 GMT date: Wed, 26 Nov 2025 13:09:34 GMT --- lib/src/StaticFileRouter.cc | 27 +++++++++++++++++++++-- lib/tests/integration_test/client/main.cc | 9 ++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/src/StaticFileRouter.cc b/lib/src/StaticFileRouter.cc index 4d89793633..7210b115d2 100644 --- a/lib/src/StaticFileRouter.cc +++ b/lib/src/StaticFileRouter.cc @@ -169,7 +169,18 @@ void StaticFileRouter::route( if (std::filesystem::is_directory(fsFilePath, err)) { // Check if path is eligible for an implicit index.html - if (implicitPageEnable_) + if (implicitPageEnable_ && req->path().back() != '/') + { + std::string newLocation = req->path() + "/"; + if (!req->query().empty()) + { + newLocation += "?" + req->query(); + } + callback(HttpResponse::newRedirectionResponse( + newLocation, k301MovedPermanently)); + return; + } + else if (implicitPageEnable_) { filePath = filePath + "/" + implicitPage_; } @@ -244,7 +255,19 @@ void StaticFileRouter::route( if (std::filesystem::is_directory(fsDirectoryPath, err)) { // Check if path is eligible for an implicit index.html - if (implicitPageEnable_) + if (implicitPageEnable_ && req->path().back() != '/') + { + std::string newLocation = req->path() + "/"; + if (!req->query().empty()) + { + newLocation += "?" + req->query(); + } + callback( + HttpResponse::newRedirectionResponse(newLocation, + k301MovedPermanently)); + return; + } + else if (implicitPageEnable_) { std::string filePath = directoryPath + "/" + implicitPage_; sendStaticFileResponse(filePath, req, std::move(callback), ""); diff --git a/lib/tests/integration_test/client/main.cc b/lib/tests/integration_test/client/main.cc index f07d2ea6f1..e7d6e05ee6 100644 --- a/lib/tests/integration_test/client/main.cc +++ b/lib/tests/integration_test/client/main.cc @@ -673,6 +673,15 @@ void doTest(const HttpClientPtr &client, std::shared_ptr TEST_CTX) req = HttpRequest::newHttpRequest(); req->setMethod(drogon::Get); req->setPath("/a-directory"); + client->sendRequest( + req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) { + REQUIRE(result == ReqResult::Ok); + CHECK(resp->getStatusCode() == k301MovedPermanently); + CHECK(resp->getHeader("Location") == "/a-directory/"); + }); + req = HttpRequest::newHttpRequest(); + req->setMethod(drogon::Get); + req->setPath("/a-directory/"); client->sendRequest(req, [req, TEST_CTX, body](ReqResult result, const HttpResponsePtr &resp) {