]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: add Request.Context and Request.WithContext
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 4 Apr 2016 20:31:08 +0000 (13:31 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 5 Apr 2016 03:14:38 +0000 (03:14 +0000)
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>
src/go/build/deps_test.go
src/net/http/request.go
src/net/http/response_test.go
src/net/http/transport.go
src/net/http/transport_test.go

index 21e12d315ece413566c91a2e65e55aaf80b7a77b..c066048630c2eb50b09ccbda79d878cc74417a34 100644 (file)
@@ -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",
        },
index d9ebb26dfc612373b7da71cf817af36c9a36fa26..371d36b0978d6264439967a06264cdc8a6d06c08 100644 (file)
@@ -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
index 354a21bc46931874e8954e536c144a975a984d08..2591e3ac81258e0d186fe87cd5f8bd1c60b93afa 100644 (file)
@@ -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)
                }
        }
 }
index d1b64c7da9a888ebbc6fa85f5da5fa62ed5898b9..7692abff47433c11973b76b08153173c6c2fb404 100644 (file)
@@ -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
                }
        }
 
index 9c2e40d7f51f6f61e36861cf2a4acfb17d341d4e..7a01dca3941ebe780c831ec29cda5d1cd9e5f106 100644 (file)
@@ -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") {