diff --git a/httplib.h b/httplib.h index ed3b7e46..e4eefb4c 100644 --- a/httplib.h +++ b/httplib.h @@ -12448,8 +12448,14 @@ inline bool Server::read_content_core( inline bool Server::handle_file_request(Request &req, Response &res) { for (const auto &entry : base_dirs_) { - // Prefix match - if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) { + // Prefix match, on a path segment boundary. A mount point of "/mount" + // covers "/mount" and "/mount/...", but must not swallow "/mountdir/...". + // One that already ends in '/' (the root mount among them) carries its own + // boundary; set_mount_point() guarantees the mount point is not empty. + if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point) && + (entry.mount_point.back() == '/' || + req.path.size() == entry.mount_point.size() || + req.path[entry.mount_point.size()] == '/')) { std::string sub_path = "/" + req.path.substr(entry.mount_point.size()); if (detail::is_valid_path(sub_path)) { auto path = entry.base_dir + sub_path; diff --git a/test/test.cc b/test/test.cc index b202abef..5275177e 100644 --- a/test/test.cc +++ b/test/test.cc @@ -9635,6 +9635,39 @@ TEST(MountTest, Unmount) { EXPECT_EQ(StatusCode::NotFound_404, res->status); } +TEST(MountTest, PathSegmentBoundary) { + Server svr; + + svr.set_mount_point("/mount2", "./www2"); + svr.set_mount_point("/", "./www"); + + auto port = svr.bind_to_any_port(HOST); + auto listen_thread = std::thread([&svr]() { svr.listen_after_bind(); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, port); + + auto res = cli.Get("/mount2/dir/test.html"); + ASSERT_TRUE(res) << "Error: " << to_string(res.error()); + EXPECT_EQ(StatusCode::OK_200, res->status); + + // "/mount2" must not match part-way through a path segment. + res = cli.Get("/mount2dir/test.html"); + ASSERT_TRUE(res) << "Error: " << to_string(res.error()); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + + // A mount point ending in '/' keeps matching every path below it. + res = cli.Get("/dir/test.html"); + ASSERT_TRUE(res) << "Error: " << to_string(res.error()); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + TEST(MountTest, Redicect) { Server svr;