]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: upon http redirect, copy Request.GetBody in new request
authorMarc-Antoine Ruel <maruel@gmail.com>
Fri, 18 Apr 2025 17:48:04 +0000 (13:48 -0400)
committerGopher Robot <gobot@golang.org>
Tue, 20 May 2025 15:40:50 +0000 (08:40 -0700)
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>
src/net/http/client.go
src/net/http/client_test.go

index a281a1ca6a95c838c1f46cc8b48550369f50e50d..43a7a06bfbb7c4db182d70ebecf40306cf368686 100644 (file)
@@ -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
                        }
 
index ec5ac7ffeb8090cbed2a413f206996bb2e309115..f2e04ca4e82522dee3a6e325b89462d618f6915f 100644 (file)
@@ -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") }