diff --git a/httplib.h b/httplib.h index 8943f25..ee9a947 100644 --- a/httplib.h +++ b/httplib.h @@ -5611,7 +5611,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { if (location.empty()) { return false; } const static std::regex re( - R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); + R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); std::smatch m; if (!std::regex_match(location, m, re)) { return false; } @@ -5620,8 +5620,9 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { auto next_scheme = m[1].str(); auto next_host = m[2].str(); - auto port_str = m[3].str(); - auto next_path = m[4].str(); + if (next_host.empty()) { next_host = m[3].str(); } + auto port_str = m[4].str(); + auto next_path = m[5].str(); auto next_port = port_; if (!port_str.empty()) { @@ -7266,7 +7267,8 @@ inline Client::Client(const char *scheme_host_port) inline Client::Client(const char *scheme_host_port, const std::string &client_cert_path, const std::string &client_key_path) { - const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)"); + const static std::regex re( + R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)"); std::cmatch m; if (std::regex_match(scheme_host_port, m, re)) { @@ -7285,8 +7287,9 @@ inline Client::Client(const char *scheme_host_port, auto is_ssl = scheme == "https"; auto host = m[2].str(); + if (host.empty()) { host = m[3].str(); } - auto port_str = m[3].str(); + auto port_str = m[4].str(); auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); if (is_ssl) { diff --git a/test/test.cc b/test/test.cc index b93b1a2..c11781e 100644 --- a/test/test.cc +++ b/test/test.cc @@ -839,6 +839,18 @@ TEST(HttpsToHttpRedirectTest3, Redirect) { EXPECT_EQ(200, res->status); } +TEST(UrlWithSpace, Redirect) { + SSLClient cli("edge.forgecdn.net"); + cli.set_follow_location(true); + + auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar"); + ASSERT_TRUE(res); + EXPECT_EQ(200, res->status); + EXPECT_EQ(18527, res->get_header_value("Content-Length")); +} + +#endif + TEST(RedirectToDifferentPort, Redirect) { Server svr8080; Server svr8081; @@ -878,16 +890,6 @@ TEST(RedirectToDifferentPort, Redirect) { ASSERT_FALSE(svr8081.is_running()); } -TEST(UrlWithSpace, Redirect) { - SSLClient cli("edge.forgecdn.net"); - cli.set_follow_location(true); - - auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar"); - ASSERT_TRUE(res); - EXPECT_EQ(200, res->status); - EXPECT_EQ(18527, res->get_header_value("Content-Length")); -} - TEST(RedirectFromPageWithContent, Redirect) { Server svr; @@ -943,7 +945,61 @@ TEST(RedirectFromPageWithContent, Redirect) { ASSERT_FALSE(svr.is_running()); } -#endif +TEST(RedirectFromPageWithContentIP6, Redirect) { + Server svr; + + svr.Get("/1", [&](const Request & /*req*/, Response &res) { + res.set_content("___", "text/plain"); + // res.set_redirect("/2"); + res.set_redirect("http://[::1]:1234/2"); + }); + + svr.Get("/2", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + auto th = std::thread([&]() { svr.listen("::1", 1234); }); + + while (!svr.is_running()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Give GET time to get a few messages. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + { + Client cli("http://[::1]:1234"); + cli.set_follow_location(true); + + std::string body; + auto res = cli.Get("/1", [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(200, res->status); + EXPECT_EQ("Hello World!", body); + } + + { + Client cli("http://[::1]:1234"); + + std::string body; + auto res = cli.Get("/1", [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(302, res->status); + EXPECT_EQ("___", body); + } + + svr.stop(); + th.join(); + ASSERT_FALSE(svr.is_running()); +} TEST(PathUrlEncodeTest, PathUrlEncode) { Server svr; @@ -2717,10 +2773,12 @@ TEST_F(ServerTest, PutLargeFileWithGzip) { TEST_F(ServerTest, PutLargeFileWithGzip2) { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - Client cli("https://localhost:1234"); + std::string s = std::string("https://") + HOST + ":" + std::to_string(PORT); + Client cli(s.c_str()); cli.enable_server_certificate_verification(false); #else - Client cli("http://localhost:1234"); + std::string s = std::string("http://") + HOST + ":" + std::to_string(PORT); + Client cli(s.c_str()); #endif cli.set_compress(true);