mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-25 12:14:41 +00:00
Return a 3xx without a Location instead of failing the redirect
With follow_location on, every 3xx was taken for a redirect, so a 304 Not Modified, or any 3xx without a Location, failed with Error::Unknown. Follow a 3xx only when it has a Location, as curl does, and return the others as the response, as curl, Go, requests and urllib3 do. Response handlers and content receivers now get such a response too.
This commit is contained in:
1 parent
906a7bf04e
commit
106edcf606
2 files changed
+81
-4
No files matched your search
@@ -16128,6 +16128,12 @@ inline bool ChunkedDecoder::parse_trailers_into(Headers &dest,
|
|||||||
return parse_trailers(lr, dest, src_headers);
|
return parse_trailers(lr, dest, src_headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A 3xx without a Location, such as a 304, is a final response
|
||||||
|
inline bool is_redirect(const Response &res) {
|
||||||
|
return 300 < res.status && res.status < 400 &&
|
||||||
|
!res.get_header_value("Location").empty();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
@@ -16185,7 +16191,7 @@ inline bool ClientImpl::handle_request(Stream &strm, Request &req,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (300 < res.status && res.status < 400 && follow_location_) {
|
if (follow_location_ && detail::is_redirect(res)) {
|
||||||
req = std::move(req_save);
|
req = std::move(req_save);
|
||||||
// The client that followed the redirect has answered any challenge it
|
// The client that followed the redirect has answered any challenge it
|
||||||
// could. Retrying below would sign another host's challenge and send it
|
// could. Retrying below would sign another host's challenge and send it
|
||||||
@@ -16931,9 +16937,8 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto redirect = 300 < res.status && res.status < 400 &&
|
auto redirect = follow_location_ && detail::is_redirect(res) &&
|
||||||
res.status != StatusCode::NotModified_304 &&
|
res.status != StatusCode::NotModified_304;
|
||||||
follow_location_;
|
|
||||||
|
|
||||||
if (req.response_handler && !redirect) {
|
if (req.response_handler && !redirect) {
|
||||||
if (!req.response_handler(res)) {
|
if (!req.response_handler(res)) {
|
||||||
|
|||||||
@@ -19849,6 +19849,78 @@ TEST(RedirectTest, LocationPathIsNotDecoded) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RedirectTest, NotModifiedIsReturned) {
|
||||||
|
Server svr;
|
||||||
|
svr.Get("/cached", [](const Request &, Response &res) {
|
||||||
|
res.status = StatusCode::NotModified_304;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto port = svr.bind_to_any_port(HOST);
|
||||||
|
auto thread = std::thread([&]() { svr.listen_after_bind(); });
|
||||||
|
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("/cached", Headers{{"If-None-Match", "\"v1\""}});
|
||||||
|
ASSERT_TRUE(res) << "Error: " << to_string(res.error());
|
||||||
|
EXPECT_EQ(StatusCode::NotModified_304, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RedirectTest, NoLocationIsReturned) {
|
||||||
|
Server svr;
|
||||||
|
svr.Get("/no-location", [](const Request &, Response &res) {
|
||||||
|
res.status = StatusCode::Found_302;
|
||||||
|
res.set_content("no location", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
auto port = svr.bind_to_any_port(HOST);
|
||||||
|
auto thread = std::thread([&]() { svr.listen_after_bind(); });
|
||||||
|
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("/no-location");
|
||||||
|
ASSERT_TRUE(res) << "Error: " << to_string(res.error());
|
||||||
|
EXPECT_EQ(StatusCode::Found_302, res->status);
|
||||||
|
EXPECT_EQ("no location", res->body);
|
||||||
|
|
||||||
|
// The callbacks see the body of the response that is returned
|
||||||
|
auto handler_called = false;
|
||||||
|
auto progress_called = false;
|
||||||
|
std::string body;
|
||||||
|
auto res2 = cli.Get(
|
||||||
|
"/no-location",
|
||||||
|
[&](const Response &) {
|
||||||
|
handler_called = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[&](const char *data, size_t data_length) {
|
||||||
|
body.append(data, data_length);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[&](size_t, size_t) {
|
||||||
|
progress_called = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(res2) << "Error: " << to_string(res2.error());
|
||||||
|
EXPECT_EQ(StatusCode::Found_302, res2->status);
|
||||||
|
EXPECT_TRUE(handler_called);
|
||||||
|
EXPECT_TRUE(progress_called);
|
||||||
|
EXPECT_EQ("no location", body);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(RedirectTest, RedirectToUrlWithQueryParameters) {
|
TEST(RedirectTest, RedirectToUrlWithQueryParameters) {
|
||||||
Server svr;
|
Server svr;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user