1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package httptrace provides mechanisms to trace the events within
6 // HTTP client requests.
19 // unique type to prevent assignment.
20 type clientEventContextKey
struct{}
22 // ContextClientTrace returns the ClientTrace associated with the
23 // provided context. If none, it returns nil.
24 func ContextClientTrace(ctx context
.Context
) *ClientTrace
{
25 trace
, _
:= ctx
.Value(clientEventContextKey
{}).(*ClientTrace
)
29 // WithClientTrace returns a new context based on the provided parent
30 // ctx. HTTP client requests made with the returned context will use
31 // the provided trace hooks, in addition to any previous hooks
32 // registered with ctx. Any hooks defined in the provided trace will
34 func WithClientTrace(ctx context
.Context
, trace
*ClientTrace
) context
.Context
{
38 old
:= ContextClientTrace(ctx
)
41 ctx
= context
.WithValue(ctx
, clientEventContextKey
{}, trace
)
42 if trace
.hasNetHooks() {
43 nt
:= &nettrace
.Trace
{
44 ConnectStart
: trace
.ConnectStart
,
45 ConnectDone
: trace
.ConnectDone
,
47 if trace
.DNSStart
!= nil {
48 nt
.DNSStart
= func(name
string) {
49 trace
.DNSStart(DNSStartInfo
{Host
: name
})
52 if trace
.DNSDone
!= nil {
53 nt
.DNSDone
= func(netIPs
[]interface{}, coalesced
bool, err error
) {
54 addrs
:= make([]net
.IPAddr
, len(netIPs
))
55 for i
, ip
:= range netIPs
{
56 addrs
[i
] = ip
.(net
.IPAddr
)
58 trace
.DNSDone(DNSDoneInfo
{
65 ctx
= context
.WithValue(ctx
, nettrace
.TraceKey
{}, nt
)
70 // ClientTrace is a set of hooks to run at various stages of an outgoing
71 // HTTP request. Any particular hook may be nil. Functions may be
72 // called concurrently from different goroutines and some may be called
73 // after the request has completed or failed.
75 // ClientTrace currently traces a single HTTP request & response
76 // during a single round trip and has no hooks that span a series
77 // of redirected requests.
79 // See https://blog.golang.org/http-tracing for more.
80 type ClientTrace
struct {
81 // GetConn is called before a connection is created or
82 // retrieved from an idle pool. The hostPort is the
83 // "host:port" of the target or proxy. GetConn is called even
84 // if there's already an idle cached connection available.
85 GetConn
func(hostPort
string)
87 // GotConn is called after a successful connection is
88 // obtained. There is no hook for failure to obtain a
89 // connection; instead, use the error from
90 // Transport.RoundTrip.
91 GotConn
func(GotConnInfo
)
93 // PutIdleConn is called when the connection is returned to
94 // the idle pool. If err is nil, the connection was
95 // successfully returned to the idle pool. If err is non-nil,
96 // it describes why not. PutIdleConn is not called if
97 // connection reuse is disabled via Transport.DisableKeepAlives.
98 // PutIdleConn is called before the caller's Response.Body.Close
100 // For HTTP/2, this hook is not currently used.
101 PutIdleConn
func(err error
)
103 // GotFirstResponseByte is called when the first byte of the response
104 // headers is available.
105 GotFirstResponseByte
func()
107 // Got100Continue is called if the server replies with a "100
108 // Continue" response.
109 Got100Continue
func()
111 // Got1xxResponse is called for each 1xx informational response header
112 // returned before the final non-1xx response. Got1xxResponse is called
113 // for "100 Continue" responses, even if Got100Continue is also defined.
114 // If it returns an error, the client request is aborted with that error value.
115 Got1xxResponse
func(code
int, header textproto
.MIMEHeader
) error
117 // DNSStart is called when a DNS lookup begins.
118 DNSStart
func(DNSStartInfo
)
120 // DNSDone is called when a DNS lookup ends.
121 DNSDone
func(DNSDoneInfo
)
123 // ConnectStart is called when a new connection's Dial begins.
124 // If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is
125 // enabled, this may be called multiple times.
126 ConnectStart
func(network
, addr
string)
128 // ConnectDone is called when a new connection's Dial
129 // completes. The provided err indicates whether the
130 // connection completedly successfully.
131 // If net.Dialer.DualStack ("Happy Eyeballs") support is
132 // enabled, this may be called multiple times.
133 ConnectDone
func(network
, addr
string, err error
)
135 // TLSHandshakeStart is called when the TLS handshake is started. When
136 // connecting to a HTTPS site via a HTTP proxy, the handshake happens after
137 // the CONNECT request is processed by the proxy.
138 TLSHandshakeStart
func()
140 // TLSHandshakeDone is called after the TLS handshake with either the
141 // successful handshake's connection state, or a non-nil error on handshake
143 TLSHandshakeDone
func(tls
.ConnectionState
, error
)
145 // WroteHeaderField is called after the Transport has written
146 // each request header. At the time of this call the values
147 // might be buffered and not yet written to the network.
148 WroteHeaderField
func(key
string, value
[]string)
150 // WroteHeaders is called after the Transport has written
151 // all request headers.
154 // Wait100Continue is called if the Request specified
155 // "Expected: 100-continue" and the Transport has written the
156 // request headers but is waiting for "100 Continue" from the
157 // server before writing the request body.
158 Wait100Continue
func()
160 // WroteRequest is called with the result of writing the
161 // request and any body. It may be called multiple times
162 // in the case of retried requests.
163 WroteRequest
func(WroteRequestInfo
)
166 // WroteRequestInfo contains information provided to the WroteRequest
168 type WroteRequestInfo
struct {
169 // Err is any error encountered while writing the Request.
173 // compose modifies t such that it respects the previously-registered hooks in old,
174 // subject to the composition policy requested in t.Compose.
175 func (t
*ClientTrace
) compose(old
*ClientTrace
) {
179 tv
:= reflect
.ValueOf(t
).Elem()
180 ov
:= reflect
.ValueOf(old
).Elem()
181 structType
:= tv
.Type()
182 for i
:= 0; i
< structType
.NumField(); i
++ {
184 hookType
:= tf
.Type()
185 if hookType
.Kind() != reflect
.Func
{
197 // Make a copy of tf for tf to call. (Otherwise it
198 // creates a recursive call cycle and stack overflows)
199 tfCopy
:= reflect
.ValueOf(tf
.Interface())
201 // We need to call both tf and of in some order.
202 newFunc
:= reflect
.MakeFunc(hookType
, func(args
[]reflect
.Value
) []reflect
.Value
{
206 tv
.Field(i
).Set(newFunc
)
210 // DNSStartInfo contains information about a DNS request.
211 type DNSStartInfo
struct {
215 // DNSDoneInfo contains information about the results of a DNS lookup.
216 type DNSDoneInfo
struct {
217 // Addrs are the IPv4 and/or IPv6 addresses found in the DNS
218 // lookup. The contents of the slice should not be mutated.
221 // Err is any error that occurred during the DNS lookup.
224 // Coalesced is whether the Addrs were shared with another
225 // caller who was doing the same DNS lookup concurrently.
229 func (t
*ClientTrace
) hasNetHooks() bool {
233 return t
.DNSStart
!= nil || t
.DNSDone
!= nil || t
.ConnectStart
!= nil || t
.ConnectDone
!= nil
236 // GotConnInfo is the argument to the ClientTrace.GotConn function and
237 // contains information about the obtained connection.
238 type GotConnInfo
struct {
239 // Conn is the connection that was obtained. It is owned by
240 // the http.Transport and should not be read, written or
241 // closed by users of ClientTrace.
244 // Reused is whether this connection has been previously
245 // used for another HTTP request.
248 // WasIdle is whether this connection was obtained from an
252 // IdleTime reports how long the connection was previously
253 // idle, if WasIdle is true.
254 IdleTime time
.Duration