Commit 1119af89 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/http: update bundled x/net/http2 for httptrace changes

Updates x/net/http2 to 3b99394 for golang.org/cl/23205

And associated tests.

Fixes #12580

Change-Id: I1f4b59267b453d241f2afaa315b7fe10d477e52d
Reviewed-on: https://go-review.googlesource.com/23206Reviewed-by: default avatarAndrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 4d2ac544
......@@ -44,6 +44,13 @@ func (t *clientServerTest) close() {
t.ts.Close()
}
func (t *clientServerTest) scheme() string {
if t.h2 {
return "https"
}
return "http"
}
const (
h1Mode = false
h2Mode = true
......
......@@ -30,6 +30,7 @@ import (
"io/ioutil"
"log"
"net"
"net/http/httptrace"
"net/textproto"
"net/url"
"os"
......@@ -1973,10 +1974,52 @@ func http2summarizeFrame(f http2Frame) string {
return buf.String()
}
type http2clientTrace httptrace.ClientTrace
func http2reqContext(r *Request) context.Context { return r.Context() }
func http2setResponseUncompressed(res *Response) { res.Uncompressed = true }
func http2traceGotConn(req *Request, cc *http2ClientConn) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GotConn == nil {
return
}
ci := httptrace.GotConnInfo{Conn: cc.tconn}
cc.mu.Lock()
ci.Reused = cc.nextStreamID > 1
ci.WasIdle = len(cc.streams) == 0
if ci.WasIdle {
ci.IdleTime = time.Now().Sub(cc.lastActive)
}
cc.mu.Unlock()
trace.GotConn(ci)
}
func http2traceWroteHeaders(trace *http2clientTrace) {
if trace != nil && trace.WroteHeaders != nil {
trace.WroteHeaders()
}
}
func http2traceWroteRequest(trace *http2clientTrace, err error) {
if trace != nil && trace.WroteRequest != nil {
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
}
}
func http2traceFirstResponseByte(trace *http2clientTrace) {
if trace != nil && trace.GotFirstResponseByte != nil {
trace.GotFirstResponseByte()
}
}
func http2requestTrace(req *Request) *http2clientTrace {
trace := httptrace.ContextClientTrace(req.Context())
return (*http2clientTrace)(trace)
}
var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
type http2goroutineLock uint64
......@@ -4879,6 +4922,8 @@ type http2ClientConn struct {
bw *bufio.Writer
br *bufio.Reader
fr *http2Framer
lastActive time.Time
// Settings from peer:
maxFrameSize uint32
maxConcurrentStreams uint32
......@@ -4896,6 +4941,7 @@ type http2ClientConn struct {
type http2clientStream struct {
cc *http2ClientConn
req *Request
trace *http2clientTrace // or nil
ID uint32
resc chan http2resAndError
bufPipe http2pipe // buffered pipe with the flow-controlled response payload
......@@ -5014,6 +5060,7 @@ func (t *http2Transport) RoundTripOpt(req *Request, opt http2RoundTripOpt) (*Res
t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
return nil, err
}
http2traceGotConn(req, cc)
res, err := cc.RoundTrip(req)
if http2shouldRetryRequest(req, err) {
continue
......@@ -5335,6 +5382,7 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
}
cc.mu.Lock()
cc.lastActive = time.Now()
if cc.closed || !cc.canTakeNewRequestLocked() {
cc.mu.Unlock()
return nil, http2errClientConnUnusable
......@@ -5342,6 +5390,7 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
cs := cc.newStream()
cs.req = req
cs.trace = http2requestTrace(req)
hasBody := body != nil
if !cc.t.disableCompression() &&
......@@ -5357,6 +5406,7 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
endStream := !hasBody && !hasTrailers
werr := cc.writeHeaders(cs.ID, endStream, hdrs)
cc.wmu.Unlock()
http2traceWroteHeaders(cs.trace)
cc.mu.Unlock()
if werr != nil {
......@@ -5365,6 +5415,7 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
}
cc.forgetStreamID(cs.ID)
http2traceWroteRequest(cs.trace, werr)
return nil, werr
}
......@@ -5376,6 +5427,7 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
bodyCopyErrc <- cs.writeRequestBody(body, req.Body)
}()
} else {
http2traceWroteRequest(cs.trace, nil)
if d := cc.responseHeaderTimeout(); d != 0 {
timer := time.NewTimer(d)
defer timer.Stop()
......@@ -5430,6 +5482,7 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
return nil, cs.resetErr
case err := <-bodyCopyErrc:
http2traceWroteRequest(cs.trace, err)
if err != nil {
return nil, err
}
......@@ -5729,6 +5782,7 @@ func (cc *http2ClientConn) streamByID(id uint32, andRemove bool) *http2clientStr
defer cc.mu.Unlock()
cs := cc.streams[id]
if andRemove && cs != nil && !cc.closed {
cc.lastActive = time.Now()
delete(cc.streams, id)
close(cs.done)
}
......@@ -5852,6 +5906,10 @@ func (rl *http2clientConnReadLoop) processHeaders(f *http2MetaHeadersFrame) erro
} else {
return rl.processTrailers(cs, f)
}
if cs.trace != nil {
http2traceFirstResponseByte(cs.trace)
}
res, err := rl.handleResponse(cs, f)
if err != nil {
......
......@@ -90,6 +90,7 @@ type ClientTrace struct {
// connection reuse is disabled via Transport.DisableKeepAlives.
// PutIdleConn is called before the caller's Response.Body.Close
// call returns.
// For HTTP/2, this hook is not currently used.
PutIdleConn func(err error)
// GotFirstResponseByte is called when the first byte of the response
......
......@@ -3193,26 +3193,26 @@ func TestTransportResponseHeaderLength(t *testing.T) {
}
}
func TestTransportEventTrace(t *testing.T) { testTransportEventTrace(t, false) }
func TestTransportEventTrace(t *testing.T) { testTransportEventTrace(t, h1Mode, false) }
func TestTransportEventTrace_h2(t *testing.T) { testTransportEventTrace(t, h2Mode, false) }
// test a non-nil httptrace.ClientTrace but with all hooks set to zero.
func TestTransportEventTrace_NoHooks(t *testing.T) { testTransportEventTrace(t, true) }
func TestTransportEventTrace_NoHooks(t *testing.T) { testTransportEventTrace(t, h1Mode, true) }
func TestTransportEventTrace_NoHooks_h2(t *testing.T) { testTransportEventTrace(t, h2Mode, true) }
func testTransportEventTrace(t *testing.T, noHooks bool) {
func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) {
defer afterTest(t)
const resBody = "some body"
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
if _, err := ioutil.ReadAll(r.Body); err != nil {
t.Error(err)
}
io.WriteString(w, resBody)
}))
defer ts.Close()
tr := &Transport{
ExpectContinueTimeout: 1 * time.Second,
defer cst.close()
if !h2 {
cst.tr.ExpectContinueTimeout = 1 * time.Second
}
defer tr.CloseIdleConnections()
c := &Client{Transport: tr}
var mu sync.Mutex
var buf bytes.Buffer
......@@ -3223,7 +3223,8 @@ func testTransportEventTrace(t *testing.T, noHooks bool) {
buf.WriteByte('\n')
}
ip, port, err := net.SplitHostPort(ts.Listener.Addr().String())
addrStr := cst.ts.Listener.Addr().String()
ip, port, err := net.SplitHostPort(addrStr)
if err != nil {
t.Fatal(err)
}
......@@ -3237,7 +3238,7 @@ func testTransportEventTrace(t *testing.T, noHooks bool) {
return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
})
req, _ := NewRequest("POST", "http://dns-is-faked.golang:"+port, strings.NewReader("some body"))
req, _ := NewRequest("POST", cst.scheme()+"://dns-is-faked.golang:"+port, strings.NewReader("some body"))
trace := &httptrace.ClientTrace{
GetConn: func(hostPort string) { logf("Getting conn for %v ...", hostPort) },
GotConn: func(ci httptrace.GotConnInfo) { logf("got conn: %+v", ci) },
......@@ -3263,7 +3264,7 @@ func testTransportEventTrace(t *testing.T, noHooks bool) {
req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
req.Header.Set("Expect", "100-continue")
res, err := c.Do(req)
res, err := cst.c.Do(req)
if err != nil {
t.Fatal(err)
}
......@@ -3292,14 +3293,17 @@ func testTransportEventTrace(t *testing.T, noHooks bool) {
wantSub("Getting conn for dns-is-faked.golang:" + port)
wantSub("DNS start: {Host:dns-is-faked.golang}")
wantSub("DNS done: {Addrs:[{IP:" + ip + " Zone:}] Err:<nil> Coalesced:false}")
wantSub("Connecting to tcp " + ts.Listener.Addr().String())
wantSub("connected to tcp " + ts.Listener.Addr().String() + " = <nil>")
wantSub("Connecting to tcp " + addrStr)
wantSub("connected to tcp " + addrStr + " = <nil>")
wantSub("Reused:false WasIdle:false IdleTime:0s")
wantSub("first response byte")
if !h2 {
wantSub("PutIdleConn = <nil>")
wantSub("WroteRequest: {Err:<nil>}")
// TODO: implement these next two for Issue 13851
wantSub("Wait100Continue")
wantSub("Got100Continue")
}
wantSub("WroteRequest: {Err:<nil>}")
if strings.Contains(got, " to udp ") {
t.Errorf("should not see UDP (DNS) connections")
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment