diff --git a/httplib.h b/httplib.h index 749fa61768..e91bbf217f 100644 --- a/httplib.h +++ b/httplib.h @@ -8636,7 +8636,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { if (location.empty()) { return false; } thread_local const std::regex re( - R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)"); + R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(?:\?([^#]*))?(?:#.*)?)"); std::smatch m; if (!std::regex_match(location, m, re)) { return false; } @@ -8661,16 +8661,16 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { if (next_host.empty()) { next_host = host_; } if (next_path.empty()) { next_path = "/"; } - auto path = detail::decode_path(next_path, true) + next_query; + if (!next_query.empty()) { detail::parse_query_text(next_query, req.params); } // Same host redirect - use current client if (next_scheme == scheme && next_host == host_ && next_port == port_) { - return detail::redirect(*this, req, res, path, location, error); + return detail::redirect(*this, req, res, next_path, location, error); } // Cross-host/scheme redirect - create new client with robust setup return create_redirect_client(next_scheme, next_host, next_port, req, res, - path, location, error); + next_path, location, error); } // New method for robust redirect client creation diff --git a/test/test.cc b/test/test.cc index f7624bf365..db0db934f0 100644 --- a/test/test.cc +++ b/test/test.cc @@ -9942,6 +9942,37 @@ TEST(RedirectTest, RedirectToUrlWithQueryParameters) { } } +TEST(RedirectTest, RedirectToUrlWithPlusInQueryParameters) { + Server svr; + + svr.Get("/", [](const Request & /*req*/, Response &res) { + res.set_redirect(R"(/hello?key=AByz09+~-._%20%26%3F%C3%BC%2B)"); + }); + + svr.Get("/hello", [](const Request &req, Response &res) { + res.set_content(req.get_param_value("key"), "text/plain"); + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + cli.set_follow_location(true); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("AByz09 ~-._ &?ΓΌ+", res->body); + } +} + TEST(VulnerabilityTest, CRLFInjection) { Server svr;