aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc-Antoine Ruel <maruel@gmail.com>2025-04-18 13:48:04 -0400
committerGopher Robot <gobot@golang.org>2025-05-20 08:40:50 -0700
commitb69f50faef360beedd408048d19909c85a2e0de0 (patch)
treeef4400f97df84f81887f67faad66e3ec11a8f8fe
parentdf9888ea4e97feb755e452609be5078686370995 (diff)
downloadgo-b69f50faef360beedd408048d19909c85a2e0de0.tar.xz
net/http: upon http redirect, copy Request.GetBody in new request
This enable http.RoundTripper implementation to retry POST request (let's say after a 500) after a 307/308 redirect. Fixes #73439 Change-Id: I4365ff58b012c7f0d60e0317a08c98b1d48f657e Reviewed-on: https://go-review.googlesource.com/c/go/+/666735 Reviewed-by: Sean Liao <sean@liao.dev> Auto-Submit: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Damien Neil <dneil@google.com>
-rw-r--r--src/net/http/client.go1
-rw-r--r--src/net/http/client_test.go55
2 files changed, 56 insertions, 0 deletions
diff --git a/src/net/http/client.go b/src/net/http/client.go
index a281a1ca6a..43a7a06bfb 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -672,6 +672,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
resp.closeBody()
return nil, uerr(err)
}
+ req.GetBody = ireq.GetBody
req.ContentLength = ireq.ContentLength
}
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index ec5ac7ffeb..f2e04ca4e8 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -1962,6 +1962,61 @@ func testTransportBodyReadError(t *testing.T, mode testMode) {
}
}
+// Make sure the retries copies the GetBody in the request.
+func TestRedirectGetBody(t *testing.T) { run(t, testRedirectGetBody) }
+
+func testRedirectGetBody(t *testing.T, mode testMode) {
+ ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
+ b, err := io.ReadAll(r.Body)
+ if err != nil {
+ t.Error(err)
+ }
+ if err = r.Body.Close(); err != nil {
+ t.Error(err)
+ }
+ if s := string(b); s != "hello" {
+ t.Errorf("expected hello, got %s", s)
+ }
+ if r.URL.Path == "/first" {
+ Redirect(w, r, "/second", StatusTemporaryRedirect)
+ return
+ }
+ w.Write([]byte("world"))
+ })).ts
+ c := ts.Client()
+ c.Transport = &roundTripperGetBody{c.Transport, t}
+ req, err := NewRequest("POST", ts.URL+"/first", strings.NewReader("hello"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err := c.Do(req.WithT(t))
+ if err != nil {
+ t.Fatal(err)
+ }
+ b, err := io.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = res.Body.Close(); err != nil {
+ t.Fatal(err)
+ }
+ if s := string(b); s != "world" {
+ t.Fatalf("expected world, got %s", s)
+ }
+}
+
+type roundTripperGetBody struct {
+ Transport RoundTripper
+ t *testing.T
+}
+
+func (r *roundTripperGetBody) RoundTrip(req *Request) (*Response, error) {
+ if req.GetBody == nil {
+ r.t.Error("missing Request.GetBody")
+ }
+ return r.Transport.RoundTrip(req)
+}
+
type roundTripperWithoutCloseIdle struct{}
func (roundTripperWithoutCloseIdle) RoundTrip(*Request) (*Response, error) { panic("unused") }