Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/http/export_test.go

Documentation: net/http

     1  // Copyright 2011 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  // Bridge package to expose http internals to tests in the http_test
     6  // package.
     7  
     8  package http
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  	"net"
    14  	"net/url"
    15  	"sort"
    16  	"sync"
    17  	"testing"
    18  	"time"
    19  )
    20  
    21  var (
    22  	DefaultUserAgent                  = defaultUserAgent
    23  	NewLoggingConn                    = newLoggingConn
    24  	ExportAppendTime                  = appendTime
    25  	ExportRefererForURL               = refererForURL
    26  	ExportServerNewConn               = (*Server).newConn
    27  	ExportCloseWriteAndWait           = (*conn).closeWriteAndWait
    28  	ExportErrRequestCanceled          = errRequestCanceled
    29  	ExportErrRequestCanceledConn      = errRequestCanceledConn
    30  	ExportErrServerClosedIdle         = errServerClosedIdle
    31  	ExportServeFile                   = serveFile
    32  	ExportScanETag                    = scanETag
    33  	ExportHttp2ConfigureServer        = http2ConfigureServer
    34  	Export_shouldCopyHeaderOnRedirect = shouldCopyHeaderOnRedirect
    35  	Export_writeStatusLine            = writeStatusLine
    36  	Export_is408Message               = is408Message
    37  )
    38  
    39  const MaxWriteWaitBeforeConnReuse = maxWriteWaitBeforeConnReuse
    40  
    41  func init() {
    42  	// We only want to pay for this cost during testing.
    43  	// When not under test, these values are always nil
    44  	// and never assigned to.
    45  	testHookMu = new(sync.Mutex)
    46  
    47  	testHookClientDoResult = func(res *Response, err error) {
    48  		if err != nil {
    49  			if _, ok := err.(*url.Error); !ok {
    50  				panic(fmt.Sprintf("unexpected Client.Do error of type %T; want *url.Error", err))
    51  			}
    52  		} else {
    53  			if res == nil {
    54  				panic("Client.Do returned nil, nil")
    55  			}
    56  			if res.Body == nil {
    57  				panic("Client.Do returned nil res.Body and no error")
    58  			}
    59  		}
    60  	}
    61  }
    62  
    63  func CondSkipHTTP2(t *testing.T) {
    64  	if omitBundledHTTP2 {
    65  		t.Skip("skipping HTTP/2 test when nethttpomithttp2 build tag in use")
    66  	}
    67  }
    68  
    69  var (
    70  	SetEnterRoundTripHook = hookSetter(&testHookEnterRoundTrip)
    71  	SetRoundTripRetried   = hookSetter(&testHookRoundTripRetried)
    72  )
    73  
    74  func SetReadLoopBeforeNextReadHook(f func()) {
    75  	testHookMu.Lock()
    76  	defer testHookMu.Unlock()
    77  	unnilTestHook(&f)
    78  	testHookReadLoopBeforeNextRead = f
    79  }
    80  
    81  // SetPendingDialHooks sets the hooks that run before and after handling
    82  // pending dials.
    83  func SetPendingDialHooks(before, after func()) {
    84  	unnilTestHook(&before)
    85  	unnilTestHook(&after)
    86  	testHookPrePendingDial, testHookPostPendingDial = before, after
    87  }
    88  
    89  func SetTestHookServerServe(fn func(*Server, net.Listener)) { testHookServerServe = fn }
    90  
    91  func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
    92  	ctx, cancel := context.WithCancel(context.Background())
    93  	go func() {
    94  		<-ch
    95  		cancel()
    96  	}()
    97  	return &timeoutHandler{
    98  		handler:     handler,
    99  		testContext: ctx,
   100  		// (no body)
   101  	}
   102  }
   103  
   104  func ResetCachedEnvironment() {
   105  	resetProxyConfig()
   106  }
   107  
   108  func (t *Transport) NumPendingRequestsForTesting() int {
   109  	t.reqMu.Lock()
   110  	defer t.reqMu.Unlock()
   111  	return len(t.reqCanceler)
   112  }
   113  
   114  func (t *Transport) IdleConnKeysForTesting() (keys []string) {
   115  	keys = make([]string, 0)
   116  	t.idleMu.Lock()
   117  	defer t.idleMu.Unlock()
   118  	for key := range t.idleConn {
   119  		keys = append(keys, key.String())
   120  	}
   121  	sort.Strings(keys)
   122  	return
   123  }
   124  
   125  func (t *Transport) IdleConnKeyCountForTesting() int {
   126  	t.idleMu.Lock()
   127  	defer t.idleMu.Unlock()
   128  	return len(t.idleConn)
   129  }
   130  
   131  func (t *Transport) IdleConnStrsForTesting() []string {
   132  	var ret []string
   133  	t.idleMu.Lock()
   134  	defer t.idleMu.Unlock()
   135  	for _, conns := range t.idleConn {
   136  		for _, pc := range conns {
   137  			ret = append(ret, pc.conn.LocalAddr().String()+"/"+pc.conn.RemoteAddr().String())
   138  		}
   139  	}
   140  	sort.Strings(ret)
   141  	return ret
   142  }
   143  
   144  func (t *Transport) IdleConnStrsForTesting_h2() []string {
   145  	var ret []string
   146  	noDialPool := t.h2transport.(*http2Transport).ConnPool.(http2noDialClientConnPool)
   147  	pool := noDialPool.http2clientConnPool
   148  
   149  	pool.mu.Lock()
   150  	defer pool.mu.Unlock()
   151  
   152  	for k, cc := range pool.conns {
   153  		for range cc {
   154  			ret = append(ret, k)
   155  		}
   156  	}
   157  
   158  	sort.Strings(ret)
   159  	return ret
   160  }
   161  
   162  func (t *Transport) IdleConnCountForTesting(scheme, addr string) int {
   163  	t.idleMu.Lock()
   164  	defer t.idleMu.Unlock()
   165  	key := connectMethodKey{"", scheme, addr, false}
   166  	cacheKey := key.String()
   167  	for k, conns := range t.idleConn {
   168  		if k.String() == cacheKey {
   169  			return len(conns)
   170  		}
   171  	}
   172  	return 0
   173  }
   174  
   175  func (t *Transport) IdleConnWaitMapSizeForTesting() int {
   176  	t.idleMu.Lock()
   177  	defer t.idleMu.Unlock()
   178  	return len(t.idleConnWait)
   179  }
   180  
   181  func (t *Transport) IsIdleForTesting() bool {
   182  	t.idleMu.Lock()
   183  	defer t.idleMu.Unlock()
   184  	return t.closeIdle
   185  }
   186  
   187  func (t *Transport) QueueForIdleConnForTesting() {
   188  	t.queueForIdleConn(nil)
   189  }
   190  
   191  // PutIdleTestConn reports whether it was able to insert a fresh
   192  // persistConn for scheme, addr into the idle connection pool.
   193  func (t *Transport) PutIdleTestConn(scheme, addr string) bool {
   194  	c, _ := net.Pipe()
   195  	key := connectMethodKey{"", scheme, addr, false}
   196  
   197  	if t.MaxConnsPerHost > 0 {
   198  		// Transport is tracking conns-per-host.
   199  		// Increment connection count to account
   200  		// for new persistConn created below.
   201  		t.connsPerHostMu.Lock()
   202  		if t.connsPerHost == nil {
   203  			t.connsPerHost = make(map[connectMethodKey]int)
   204  		}
   205  		t.connsPerHost[key]++
   206  		t.connsPerHostMu.Unlock()
   207  	}
   208  
   209  	return t.tryPutIdleConn(&persistConn{
   210  		t:        t,
   211  		conn:     c,                   // dummy
   212  		closech:  make(chan struct{}), // so it can be closed
   213  		cacheKey: key,
   214  	}) == nil
   215  }
   216  
   217  // PutIdleTestConnH2 reports whether it was able to insert a fresh
   218  // HTTP/2 persistConn for scheme, addr into the idle connection pool.
   219  func (t *Transport) PutIdleTestConnH2(scheme, addr string, alt RoundTripper) bool {
   220  	key := connectMethodKey{"", scheme, addr, false}
   221  
   222  	if t.MaxConnsPerHost > 0 {
   223  		// Transport is tracking conns-per-host.
   224  		// Increment connection count to account
   225  		// for new persistConn created below.
   226  		t.connsPerHostMu.Lock()
   227  		if t.connsPerHost == nil {
   228  			t.connsPerHost = make(map[connectMethodKey]int)
   229  		}
   230  		t.connsPerHost[key]++
   231  		t.connsPerHostMu.Unlock()
   232  	}
   233  
   234  	return t.tryPutIdleConn(&persistConn{
   235  		t:        t,
   236  		alt:      alt,
   237  		cacheKey: key,
   238  	}) == nil
   239  }
   240  
   241  // All test hooks must be non-nil so they can be called directly,
   242  // but the tests use nil to mean hook disabled.
   243  func unnilTestHook(f *func()) {
   244  	if *f == nil {
   245  		*f = nop
   246  	}
   247  }
   248  
   249  func hookSetter(dst *func()) func(func()) {
   250  	return func(fn func()) {
   251  		unnilTestHook(&fn)
   252  		*dst = fn
   253  	}
   254  }
   255  
   256  func ExportHttp2ConfigureTransport(t *Transport) error {
   257  	t2, err := http2configureTransports(t)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	t.h2transport = t2
   262  	return nil
   263  }
   264  
   265  func (s *Server) ExportAllConnsIdle() bool {
   266  	s.mu.Lock()
   267  	defer s.mu.Unlock()
   268  	for c := range s.activeConn {
   269  		st, unixSec := c.getState()
   270  		if unixSec == 0 || st != StateIdle {
   271  			return false
   272  		}
   273  	}
   274  	return true
   275  }
   276  
   277  func (s *Server) ExportAllConnsByState() map[ConnState]int {
   278  	states := map[ConnState]int{}
   279  	s.mu.Lock()
   280  	defer s.mu.Unlock()
   281  	for c := range s.activeConn {
   282  		st, _ := c.getState()
   283  		states[st] += 1
   284  	}
   285  	return states
   286  }
   287  
   288  func (r *Request) WithT(t *testing.T) *Request {
   289  	return r.WithContext(context.WithValue(r.Context(), tLogKey{}, t.Logf))
   290  }
   291  
   292  func ExportSetH2GoawayTimeout(d time.Duration) (restore func()) {
   293  	old := http2goAwayTimeout
   294  	http2goAwayTimeout = d
   295  	return func() { http2goAwayTimeout = old }
   296  }
   297  
   298  func (r *Request) ExportIsReplayable() bool { return r.isReplayable() }
   299  
   300  // ExportCloseTransportConnsAbruptly closes all idle connections from
   301  // tr in an abrupt way, just reaching into the underlying Conns and
   302  // closing them, without telling the Transport or its persistConns
   303  // that it's doing so. This is to simulate the server closing connections
   304  // on the Transport.
   305  func ExportCloseTransportConnsAbruptly(tr *Transport) {
   306  	tr.idleMu.Lock()
   307  	for _, pcs := range tr.idleConn {
   308  		for _, pc := range pcs {
   309  			pc.conn.Close()
   310  		}
   311  	}
   312  	tr.idleMu.Unlock()
   313  }
   314  

View as plain text