...

Source file src/net/http/httptrace/trace.go

Documentation: net/http/httptrace

		 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.
		 4  
		 5  // Package httptrace provides mechanisms to trace the events within
		 6  // HTTP client requests.
		 7  package httptrace
		 8  
		 9  import (
		10  	"context"
		11  	"crypto/tls"
		12  	"internal/nettrace"
		13  	"net"
		14  	"net/textproto"
		15  	"reflect"
		16  	"time"
		17  )
		18  
		19  // unique type to prevent assignment.
		20  type clientEventContextKey struct{}
		21  
		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)
		26  	return trace
		27  }
		28  
		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
		33  // be called first.
		34  func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context {
		35  	if trace == nil {
		36  		panic("nil trace")
		37  	}
		38  	old := ContextClientTrace(ctx)
		39  	trace.compose(old)
		40  
		41  	ctx = context.WithValue(ctx, clientEventContextKey{}, trace)
		42  	if trace.hasNetHooks() {
		43  		nt := &nettrace.Trace{
		44  			ConnectStart: trace.ConnectStart,
		45  			ConnectDone:	trace.ConnectDone,
		46  		}
		47  		if trace.DNSStart != nil {
		48  			nt.DNSStart = func(name string) {
		49  				trace.DNSStart(DNSStartInfo{Host: name})
		50  			}
		51  		}
		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)
		57  				}
		58  				trace.DNSDone(DNSDoneInfo{
		59  					Addrs:		 addrs,
		60  					Coalesced: coalesced,
		61  					Err:			 err,
		62  				})
		63  			}
		64  		}
		65  		ctx = context.WithValue(ctx, nettrace.TraceKey{}, nt)
		66  	}
		67  	return ctx
		68  }
		69  
		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.
		74  //
		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.
		78  //
		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)
		86  
		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)
		92  
		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
		99  	// call returns.
	 100  	// For HTTP/2, this hook is not currently used.
	 101  	PutIdleConn func(err error)
	 102  
	 103  	// GotFirstResponseByte is called when the first byte of the response
	 104  	// headers is available.
	 105  	GotFirstResponseByte func()
	 106  
	 107  	// Got100Continue is called if the server replies with a "100
	 108  	// Continue" response.
	 109  	Got100Continue func()
	 110  
	 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
	 116  
	 117  	// DNSStart is called when a DNS lookup begins.
	 118  	DNSStart func(DNSStartInfo)
	 119  
	 120  	// DNSDone is called when a DNS lookup ends.
	 121  	DNSDone func(DNSDoneInfo)
	 122  
	 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)
	 127  
	 128  	// ConnectDone is called when a new connection's Dial
	 129  	// completes. The provided err indicates whether the
	 130  	// connection completed 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)
	 134  
	 135  	// TLSHandshakeStart is called when the TLS handshake is started. When
	 136  	// connecting to an HTTPS site via an HTTP proxy, the handshake happens
	 137  	// after the CONNECT request is processed by the proxy.
	 138  	TLSHandshakeStart func()
	 139  
	 140  	// TLSHandshakeDone is called after the TLS handshake with either the
	 141  	// successful handshake's connection state, or a non-nil error on handshake
	 142  	// failure.
	 143  	TLSHandshakeDone func(tls.ConnectionState, error)
	 144  
	 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)
	 149  
	 150  	// WroteHeaders is called after the Transport has written
	 151  	// all request headers.
	 152  	WroteHeaders func()
	 153  
	 154  	// Wait100Continue is called if the Request specified
	 155  	// "Expect: 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()
	 159  
	 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)
	 164  }
	 165  
	 166  // WroteRequestInfo contains information provided to the WroteRequest
	 167  // hook.
	 168  type WroteRequestInfo struct {
	 169  	// Err is any error encountered while writing the Request.
	 170  	Err error
	 171  }
	 172  
	 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) {
	 176  	if old == nil {
	 177  		return
	 178  	}
	 179  	tv := reflect.ValueOf(t).Elem()
	 180  	ov := reflect.ValueOf(old).Elem()
	 181  	structType := tv.Type()
	 182  	for i := 0; i < structType.NumField(); i++ {
	 183  		tf := tv.Field(i)
	 184  		hookType := tf.Type()
	 185  		if hookType.Kind() != reflect.Func {
	 186  			continue
	 187  		}
	 188  		of := ov.Field(i)
	 189  		if of.IsNil() {
	 190  			continue
	 191  		}
	 192  		if tf.IsNil() {
	 193  			tf.Set(of)
	 194  			continue
	 195  		}
	 196  
	 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())
	 200  
	 201  		// We need to call both tf and of in some order.
	 202  		newFunc := reflect.MakeFunc(hookType, func(args []reflect.Value) []reflect.Value {
	 203  			tfCopy.Call(args)
	 204  			return of.Call(args)
	 205  		})
	 206  		tv.Field(i).Set(newFunc)
	 207  	}
	 208  }
	 209  
	 210  // DNSStartInfo contains information about a DNS request.
	 211  type DNSStartInfo struct {
	 212  	Host string
	 213  }
	 214  
	 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.
	 219  	Addrs []net.IPAddr
	 220  
	 221  	// Err is any error that occurred during the DNS lookup.
	 222  	Err error
	 223  
	 224  	// Coalesced is whether the Addrs were shared with another
	 225  	// caller who was doing the same DNS lookup concurrently.
	 226  	Coalesced bool
	 227  }
	 228  
	 229  func (t *ClientTrace) hasNetHooks() bool {
	 230  	if t == nil {
	 231  		return false
	 232  	}
	 233  	return t.DNSStart != nil || t.DNSDone != nil || t.ConnectStart != nil || t.ConnectDone != nil
	 234  }
	 235  
	 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.
	 242  	Conn net.Conn
	 243  
	 244  	// Reused is whether this connection has been previously
	 245  	// used for another HTTP request.
	 246  	Reused bool
	 247  
	 248  	// WasIdle is whether this connection was obtained from an
	 249  	// idle pool.
	 250  	WasIdle bool
	 251  
	 252  	// IdleTime reports how long the connection was previously
	 253  	// idle, if WasIdle is true.
	 254  	IdleTime time.Duration
	 255  }
	 256  

View as plain text