Skip to content

Commit

Permalink
net/http: fix and lock-in Client.Do docs on request cancelation
Browse files Browse the repository at this point in the history
Fixes the docs to correctly match the implementation
and also adds a test locking-in the behavior to prevent
any accidental future regressions on the docs.

Fixes #33545

Change-Id: I6fdac6048cce8ac99beaa2db0dfc81d0dccc10f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/200798
Run-TryBot: Emmanuel Odeke <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Brad Fitzpatrick <[email protected]>
  • Loading branch information
odeke-em authored and bradfitz committed Oct 14, 2019
1 parent b649bdc commit 19e0799
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/net/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,8 @@ func Get(url string) (resp *Response, err error) {
// An error is returned if the Client's CheckRedirect function fails
// or if there was an HTTP protocol error. A non-2xx response doesn't
// cause an error. Any returned error will be of type *url.Error. The
// url.Error value's Timeout method will report true if request timed
// out or was canceled.
// url.Error value's Timeout method will report true if the request
// timed out.
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
Expand Down
55 changes: 55 additions & 0 deletions src/net/http/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1936,3 +1936,58 @@ func TestClientPropagatesTimeoutToContext(t *testing.T) {
}
c.Get("https://example.tld/")
}

func TestClientDoCanceledVsTimeout_h1(t *testing.T) {
testClientDoCanceledVsTimeout(t, h1Mode)
}

func TestClientDoCanceledVsTimeout_h2(t *testing.T) {
testClientDoCanceledVsTimeout(t, h2Mode)
}

// Issue 33545: lock-in the behavior promised by Client.Do's
// docs about request cancelation vs timing out.
func testClientDoCanceledVsTimeout(t *testing.T, h2 bool) {
defer afterTest(t)
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
w.Write([]byte("Hello, World!"))
}))
defer cst.close()

cases := []string{"timeout", "canceled"}

for _, name := range cases {
t.Run(name, func(t *testing.T) {
var ctx context.Context
var cancel func()
if name == "timeout" {
ctx, cancel = context.WithTimeout(context.Background(), -time.Nanosecond)
} else {
ctx, cancel = context.WithCancel(context.Background())
cancel()
}
defer cancel()

req, _ := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil)
_, err := cst.c.Do(req)
if err == nil {
t.Fatal("Unexpectedly got a nil error")
}

ue := err.(*url.Error)

var wantIsTimeout bool
var wantErr error = context.Canceled
if name == "timeout" {
wantErr = context.DeadlineExceeded
wantIsTimeout = true
}
if g, w := ue.Timeout(), wantIsTimeout; g != w {
t.Fatalf("url.Timeout() = %t, want %t", g, w)
}
if g, w := ue.Err, wantErr; g != w {
t.Errorf("url.Error.Err = %v; want %v", g, w)
}
})
}
}

0 comments on commit 19e0799

Please sign in to comment.