...

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

     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 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)
   134	
   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()
   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