Skip to content

Commit 3b99394

Browse files
committed
http2: when using Go 1.7, make Transport use httptrace hooks
Updates golang/go#12580 Change-Id: I95d7206a8fde494f88288b381814a5d72d42df5e Reviewed-on: https://go-review.googlesource.com/23205 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
1 parent 76e74a3 commit 3b99394

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

http2/go17.go

+44
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,52 @@ package http2
99
import (
1010
"context"
1111
"net/http"
12+
"net/http/httptrace"
13+
"time"
1214
)
1315

16+
type clientTrace httptrace.ClientTrace
17+
1418
func reqContext(r *http.Request) context.Context { return r.Context() }
1519

1620
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
21+
22+
func traceGotConn(req *http.Request, cc *ClientConn) {
23+
trace := httptrace.ContextClientTrace(req.Context())
24+
if trace == nil || trace.GotConn == nil {
25+
return
26+
}
27+
ci := httptrace.GotConnInfo{Conn: cc.tconn}
28+
cc.mu.Lock()
29+
ci.Reused = cc.nextStreamID > 1
30+
ci.WasIdle = len(cc.streams) == 0
31+
if ci.WasIdle {
32+
ci.IdleTime = time.Now().Sub(cc.lastActive)
33+
}
34+
cc.mu.Unlock()
35+
36+
trace.GotConn(ci)
37+
}
38+
39+
func traceWroteHeaders(trace *clientTrace) {
40+
if trace != nil && trace.WroteHeaders != nil {
41+
trace.WroteHeaders()
42+
}
43+
}
44+
45+
func traceWroteRequest(trace *clientTrace, err error) {
46+
if trace != nil && trace.WroteRequest != nil {
47+
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
48+
}
49+
}
50+
51+
func traceFirstResponseByte(trace *clientTrace) {
52+
if trace != nil && trace.GotFirstResponseByte != nil {
53+
trace.GotFirstResponseByte()
54+
}
55+
}
56+
57+
func requestTrace(req *http.Request) *clientTrace {
58+
trace := httptrace.ContextClientTrace(req.Context())
59+
return (*clientTrace)(trace)
60+
}

http2/not_go17.go

+8
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,11 @@ func reqContext(r *http.Request) fakeContext {
2020
func setResponseUncompressed(res *http.Response) {
2121
// Nothing.
2222
}
23+
24+
type clientTrace struct{}
25+
26+
func requestTrace(*http.Request) *clientTrace { return nil }
27+
func traceGotConn(*http.Request, *ClientConn) {}
28+
func traceFirstResponseByte(*clientTrace) {}
29+
func traceWroteHeaders(*clientTrace) {}
30+
func traceWroteRequest(*clientTrace, error) {}

http2/transport.go

+19
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ type ClientConn struct {
153153
bw *bufio.Writer
154154
br *bufio.Reader
155155
fr *Framer
156+
lastActive time.Time
157+
156158
// Settings from peer:
157159
maxFrameSize uint32
158160
maxConcurrentStreams uint32
@@ -170,6 +172,7 @@ type ClientConn struct {
170172
type clientStream struct {
171173
cc *ClientConn
172174
req *http.Request
175+
trace *clientTrace // or nil
173176
ID uint32
174177
resc chan resAndError
175178
bufPipe pipe // buffered pipe with the flow-controlled response payload
@@ -288,6 +291,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
288291
t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
289292
return nil, err
290293
}
294+
traceGotConn(req, cc)
291295
res, err := cc.RoundTrip(req)
292296
if shouldRetryRequest(req, err) {
293297
continue
@@ -622,13 +626,15 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
622626
}
623627

624628
cc.mu.Lock()
629+
cc.lastActive = time.Now()
625630
if cc.closed || !cc.canTakeNewRequestLocked() {
626631
cc.mu.Unlock()
627632
return nil, errClientConnUnusable
628633
}
629634

630635
cs := cc.newStream()
631636
cs.req = req
637+
cs.trace = requestTrace(req)
632638
hasBody := body != nil
633639

634640
// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
@@ -659,6 +665,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
659665
endStream := !hasBody && !hasTrailers
660666
werr := cc.writeHeaders(cs.ID, endStream, hdrs)
661667
cc.wmu.Unlock()
668+
traceWroteHeaders(cs.trace)
662669
cc.mu.Unlock()
663670

664671
if werr != nil {
@@ -668,6 +675,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
668675
cc.forgetStreamID(cs.ID)
669676
// Don't bother sending a RST_STREAM (our write already failed;
670677
// no need to keep writing)
678+
traceWroteRequest(cs.trace, werr)
671679
return nil, werr
672680
}
673681

@@ -679,6 +687,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
679687
bodyCopyErrc <- cs.writeRequestBody(body, req.Body)
680688
}()
681689
} else {
690+
traceWroteRequest(cs.trace, nil)
682691
if d := cc.responseHeaderTimeout(); d != 0 {
683692
timer := time.NewTimer(d)
684693
defer timer.Stop()
@@ -743,6 +752,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
743752
// forgetStreamID.
744753
return nil, cs.resetErr
745754
case err := <-bodyCopyErrc:
755+
traceWroteRequest(cs.trace, err)
746756
if err != nil {
747757
return nil, err
748758
}
@@ -1068,6 +1078,7 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
10681078
defer cc.mu.Unlock()
10691079
cs := cc.streams[id]
10701080
if andRemove && cs != nil && !cc.closed {
1081+
cc.lastActive = time.Now()
10711082
delete(cc.streams, id)
10721083
close(cs.done)
10731084
}
@@ -1196,6 +1207,13 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
11961207
} else {
11971208
return rl.processTrailers(cs, f)
11981209
}
1210+
if cs.trace != nil {
1211+
// TODO(bradfitz): move first response byte earlier,
1212+
// when we first read the 9 byte header, not waiting
1213+
// until all the HEADERS+CONTINUATION frames have been
1214+
// merged. This works for now.
1215+
traceFirstResponseByte(cs.trace)
1216+
}
11991217

12001218
res, err := rl.handleResponse(cs, f)
12011219
if err != nil {
@@ -1243,6 +1261,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
12431261
if statusCode == 100 {
12441262
// Just skip 100-continue response headers for now.
12451263
// TODO: golang.org/issue/13851 for doing it properly.
1264+
// TODO: also call the httptrace.ClientTrace hooks
12461265
cs.pastHeaders = false // do it all again
12471266
return nil, nil
12481267
}

0 commit comments

Comments
 (0)