Fix 303 redirect request bodies and Location path decoding (Fix #2606, #2607)

A 303 response turns the follow-up request into a GET, but only the buffered body and headers were cleared. A content provider (sized or chunked) stayed on the request, so the original payload was sent again, and for a chunked provider the unframed chunks also broke the keep-alive connection.

The redirect also percent-decoded the Location path before sending it. That turned %23, %3F and %25 into a fragment, a query delimiter and a different octet, so the client requested a different resource than the one named, and made set_path_encode(false) fail on any Location containing %20. The path is now sent as given.
This commit is contained in:
yhirose
2026-10-03 19:06:45 -04:00
parent 438319cfcb
commit 09fa6820fe
2 changed files with 143 additions and 9 deletions
+7 -4
View File
@@ -8976,6 +8976,9 @@ inline bool redirect(T &cli, Request &req, Response &res,
new_req.method = "GET";
new_req.body.clear();
new_req.headers.clear();
new_req.content_length_ = 0;
new_req.content_provider_ = nullptr;
new_req.is_chunked_content_provider_ = false;
}
Response new_res;
@@ -15738,7 +15741,7 @@ 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 = decode_path_component(next_path) + next_query;
auto path = std::move(next_path) + next_query;
// Same host redirect - use current client
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
@@ -15993,9 +15996,9 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
// Write request line and headers
if (detail::write_request_line(bstrm, req.method, path_with_query) < 0) {
// A rejected method (not a token, e.g. carrying CR/LF) or target (e.g.
// CR/LF smuggled in via a decoded redirect Location under
// set_path_encode(false)) must fail the request cleanly instead of
// emitting a request-line-less, header-injecting request.
// CR/LF in a caller-supplied path under set_path_encode(false)) must
// fail the request cleanly instead of emitting a request-line-less,
// header-injecting request.
error = Error::Write;
rejected_locally = true;
output_error_log(error, &req);