浏览代码

http2: make Transport use Request.Context, set Response.Uncompressed

Go 1.7 introduces Request.Context and Response.Uncompressed. Use them
both in the http2 Transport when building for Go 1.7.

Tests are in the main repo.

Updates golang/go#15366
Updates golang/go#15134

Change-Id: I4b78a2710b6cf30c769c7f85cea15ecde653a317
Reviewed-on: https://go-review.googlesource.com/23002
Reviewed-by: Andrew Gerrand <adg@golang.org>
Brad Fitzpatrick 9 年之前
父节点
当前提交
96dbb961a3
共有 3 个文件被更改,包括 60 次插入8 次删除
  1. 16 0
      http2/go17.go
  2. 22 0
      http2/not_go17.go
  3. 22 8
      http2/transport.go

+ 16 - 0
http2/go17.go

@@ -0,0 +1,16 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.7
+
+package http2
+
+import (
+	"context"
+	"net/http"
+)
+
+func reqContext(r *http.Request) context.Context { return r.Context() }
+
+func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }

+ 22 - 0
http2/not_go17.go

@@ -0,0 +1,22 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.7
+
+package http2
+
+import "net/http"
+
+type fakeContext struct{}
+
+func (fakeContext) Done() <-chan struct{} { return nil }
+func (fakeContext) Err() error            { panic("should not be called") }
+
+func reqContext(r *http.Request) fakeContext {
+	return fakeContext{}
+}
+
+func setResponseUncompressed(res *http.Response) {
+	// Nothing.
+}

+ 22 - 8
http2/transport.go

@@ -195,18 +195,22 @@ type clientStream struct {
 }
 
 // awaitRequestCancel runs in its own goroutine and waits for the user
-// to either cancel a RoundTrip request (using the provided
-// Request.Cancel channel), or for the request to be done (any way it
-// might be removed from the cc.streams map: peer reset, successful
-// completion, TCP connection breakage, etc)
-func (cs *clientStream) awaitRequestCancel(cancel <-chan struct{}) {
-	if cancel == nil {
+// to cancel a RoundTrip request, its context to expire, or for the
+// request to be done (any way it might be removed from the cc.streams
+// map: peer reset, successful completion, TCP connection breakage,
+// etc)
+func (cs *clientStream) awaitRequestCancel(req *http.Request) {
+	ctx := reqContext(req)
+	if req.Cancel == nil && ctx.Done() == nil {
 		return
 	}
 	select {
-	case <-cancel:
+	case <-req.Cancel:
 		cs.bufPipe.CloseWithError(errRequestCanceled)
 		cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
+	case <-ctx.Done():
+		cs.bufPipe.CloseWithError(ctx.Err())
+		cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
 	case <-cs.done:
 	}
 }
@@ -684,6 +688,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
 
 	readLoopResCh := cs.resc
 	bodyWritten := false
+	ctx := reqContext(req)
 
 	for {
 		select {
@@ -716,6 +721,14 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
 				cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
 			}
 			return nil, errTimeout
+		case <-ctx.Done():
+			cc.forgetStreamID(cs.ID)
+			if !hasBody || bodyWritten {
+				cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
+			} else {
+				cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
+			}
+			return nil, ctx.Err()
 		case <-req.Cancel:
 			cc.forgetStreamID(cs.ID)
 			if !hasBody || bodyWritten {
@@ -1284,13 +1297,14 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
 	cs.bufPipe = pipe{b: buf}
 	cs.bytesRemain = res.ContentLength
 	res.Body = transportResponseBody{cs}
-	go cs.awaitRequestCancel(cs.req.Cancel)
+	go cs.awaitRequestCancel(cs.req)
 
 	if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
 		res.Header.Del("Content-Encoding")
 		res.Header.Del("Content-Length")
 		res.ContentLength = -1
 		res.Body = &gzipReader{body: res.Body}
+		setResponseUncompressed(res)
 	}
 	return res, nil
 }