mirror of
https://github.com/golang/go
synced 2024-11-24 06:20:02 -07:00
net/http: add Request.Context and Request.WithContext
Currently only used by the client. The server is not yet wired up. A TODO remains to document how it works server-side, once implemented. Updates #14660 Change-Id: I27c2e74198872b2720995fa8271d91de200e23d5 Reviewed-on: https://go-review.googlesource.com/21496 Reviewed-by: Andrew Gerrand <adg@golang.org>
This commit is contained in:
parent
fcac88098b
commit
c1c7547f6a
@ -357,7 +357,8 @@ var pkgDeps = map[string][]string{
|
||||
// HTTP, kingpin of dependencies.
|
||||
"net/http": {
|
||||
"L4", "NET", "OS",
|
||||
"compress/gzip", "crypto/tls", "mime/multipart", "runtime/debug",
|
||||
"context", "compress/gzip", "crypto/tls",
|
||||
"mime/multipart", "runtime/debug",
|
||||
"net/http/internal",
|
||||
"golang.org/x/net/http2/hpack",
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ package http
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
@ -247,7 +248,43 @@ type Request struct {
|
||||
// RoundTripper may support Cancel.
|
||||
//
|
||||
// For server requests, this field is not applicable.
|
||||
//
|
||||
// Deprecated: use the Context and WithContext methods
|
||||
// instead. If a Request's Cancel field and context are both
|
||||
// set, it is undefined whether Cancel is respected.
|
||||
Cancel <-chan struct{}
|
||||
|
||||
// ctx is either the client or server context. It should only
|
||||
// be modified via copying the whole Request using WithContext.
|
||||
// It is unexported to prevent people from using Context wrong
|
||||
// and mutating the contexts held by callers of the same request.
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Context returns the request's context. To change the context, use
|
||||
// WithContext.
|
||||
//
|
||||
// The returned context is always non-nil; it defaults to the
|
||||
// background context.
|
||||
func (r *Request) Context() context.Context {
|
||||
// TODO(bradfitz): document above what Context means for server and client
|
||||
// requests, once implemented.
|
||||
if r.ctx != nil {
|
||||
return r.ctx
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
// WithContext returns a shallow copy of r with its context changed
|
||||
// to ctx. The provided ctx must be non-nil.
|
||||
func (r *Request) WithContext(ctx context.Context) *Request {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
r2 := new(Request)
|
||||
*r2 = *r
|
||||
r2.ctx = ctx
|
||||
return r2
|
||||
}
|
||||
|
||||
// ProtoAtLeast reports whether the HTTP protocol used
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"compress/gzip"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http/internal"
|
||||
@ -656,10 +657,14 @@ func diff(t *testing.T, prefix string, have, want interface{}) {
|
||||
t.Errorf("%s: type mismatch %v want %v", prefix, hv.Type(), wv.Type())
|
||||
}
|
||||
for i := 0; i < hv.NumField(); i++ {
|
||||
name := hv.Type().Field(i).Name
|
||||
if !ast.IsExported(name) {
|
||||
continue
|
||||
}
|
||||
hf := hv.Field(i).Interface()
|
||||
wf := wv.Field(i).Interface()
|
||||
if !reflect.DeepEqual(hf, wf) {
|
||||
t.Errorf("%s: %s = %v want %v", prefix, hv.Type().Field(i).Name, hf, wf)
|
||||
t.Errorf("%s: %s = %v want %v", prefix, name, hf, wf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -758,6 +758,9 @@ func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error
|
||||
case <-req.Cancel:
|
||||
handlePendingDial()
|
||||
return nil, errRequestCanceledConn
|
||||
case <-req.Context().Done():
|
||||
handlePendingDial()
|
||||
return nil, errRequestCanceledConn
|
||||
case <-cancelc:
|
||||
handlePendingDial()
|
||||
return nil, errRequestCanceledConn
|
||||
@ -1263,6 +1266,9 @@ func (pc *persistConn) readLoop() {
|
||||
case <-rc.req.Cancel:
|
||||
alive = false
|
||||
pc.t.CancelRequest(rc.req)
|
||||
case <-rc.req.Context().Done():
|
||||
alive = false
|
||||
pc.t.CancelRequest(rc.req)
|
||||
case <-pc.closech:
|
||||
alive = false
|
||||
}
|
||||
@ -1567,6 +1573,9 @@ WaitResponse:
|
||||
case <-cancelChan:
|
||||
pc.t.CancelRequest(req.Request)
|
||||
cancelChan = nil
|
||||
case <-req.Context().Done():
|
||||
pc.t.CancelRequest(req.Request)
|
||||
cancelChan = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
@ -1625,7 +1626,13 @@ func TestCancelRequestWithChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelRequestWithChannelBeforeDo(t *testing.T) {
|
||||
func TestCancelRequestWithChannelBeforeDo_Cancel(t *testing.T) {
|
||||
testCancelRequestWithChannelBeforeDo(t, false)
|
||||
}
|
||||
func TestCancelRequestWithChannelBeforeDo_Context(t *testing.T) {
|
||||
testCancelRequestWithChannelBeforeDo(t, true)
|
||||
}
|
||||
func testCancelRequestWithChannelBeforeDo(t *testing.T, withCtx bool) {
|
||||
setParallel(t)
|
||||
defer afterTest(t)
|
||||
unblockc := make(chan bool)
|
||||
@ -1646,9 +1653,15 @@ func TestCancelRequestWithChannelBeforeDo(t *testing.T) {
|
||||
c := &Client{Transport: tr}
|
||||
|
||||
req, _ := NewRequest("GET", ts.URL, nil)
|
||||
ch := make(chan struct{})
|
||||
req.Cancel = ch
|
||||
close(ch)
|
||||
if withCtx {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
req = req.WithContext(ctx)
|
||||
} else {
|
||||
ch := make(chan struct{})
|
||||
req.Cancel = ch
|
||||
close(ch)
|
||||
}
|
||||
|
||||
_, err := c.Do(req)
|
||||
if err == nil || !strings.Contains(err.Error(), "canceled") {
|
||||
|
Loading…
Reference in New Issue
Block a user