Home | History | Annotate | Download | only in 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 	"net"
     13 	"sort"
     14 	"sync"
     15 	"testing"
     16 	"time"
     17 )
     18 
     19 var (
     20 	DefaultUserAgent                  = defaultUserAgent
     21 	NewLoggingConn                    = newLoggingConn
     22 	ExportAppendTime                  = appendTime
     23 	ExportRefererForURL               = refererForURL
     24 	ExportServerNewConn               = (*Server).newConn
     25 	ExportCloseWriteAndWait           = (*conn).closeWriteAndWait
     26 	ExportErrRequestCanceled          = errRequestCanceled
     27 	ExportErrRequestCanceledConn      = errRequestCanceledConn
     28 	ExportErrServerClosedIdle         = errServerClosedIdle
     29 	ExportServeFile                   = serveFile
     30 	ExportScanETag                    = scanETag
     31 	ExportHttp2ConfigureServer        = http2ConfigureServer
     32 	Export_shouldCopyHeaderOnRedirect = shouldCopyHeaderOnRedirect
     33 	Export_writeStatusLine            = writeStatusLine
     34 )
     35 
     36 func init() {
     37 	// We only want to pay for this cost during testing.
     38 	// When not under test, these values are always nil
     39 	// and never assigned to.
     40 	testHookMu = new(sync.Mutex)
     41 }
     42 
     43 var (
     44 	SetEnterRoundTripHook = hookSetter(&testHookEnterRoundTrip)
     45 	SetRoundTripRetried   = hookSetter(&testHookRoundTripRetried)
     46 )
     47 
     48 func SetReadLoopBeforeNextReadHook(f func()) {
     49 	testHookMu.Lock()
     50 	defer testHookMu.Unlock()
     51 	unnilTestHook(&f)
     52 	testHookReadLoopBeforeNextRead = f
     53 }
     54 
     55 // SetPendingDialHooks sets the hooks that run before and after handling
     56 // pending dials.
     57 func SetPendingDialHooks(before, after func()) {
     58 	unnilTestHook(&before)
     59 	unnilTestHook(&after)
     60 	testHookPrePendingDial, testHookPostPendingDial = before, after
     61 }
     62 
     63 func SetTestHookServerServe(fn func(*Server, net.Listener)) { testHookServerServe = fn }
     64 
     65 func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
     66 	ctx, cancel := context.WithCancel(context.Background())
     67 	go func() {
     68 		<-ch
     69 		cancel()
     70 	}()
     71 	return &timeoutHandler{
     72 		handler:     handler,
     73 		testContext: ctx,
     74 		// (no body)
     75 	}
     76 }
     77 
     78 func ResetCachedEnvironment() {
     79 	httpProxyEnv.reset()
     80 	httpsProxyEnv.reset()
     81 	noProxyEnv.reset()
     82 }
     83 
     84 func (t *Transport) NumPendingRequestsForTesting() int {
     85 	t.reqMu.Lock()
     86 	defer t.reqMu.Unlock()
     87 	return len(t.reqCanceler)
     88 }
     89 
     90 func (t *Transport) IdleConnKeysForTesting() (keys []string) {
     91 	keys = make([]string, 0)
     92 	t.idleMu.Lock()
     93 	defer t.idleMu.Unlock()
     94 	for key := range t.idleConn {
     95 		keys = append(keys, key.String())
     96 	}
     97 	sort.Strings(keys)
     98 	return
     99 }
    100 
    101 func (t *Transport) IdleConnKeyCountForTesting() int {
    102 	t.idleMu.Lock()
    103 	defer t.idleMu.Unlock()
    104 	return len(t.idleConn)
    105 }
    106 
    107 func (t *Transport) IdleConnStrsForTesting() []string {
    108 	var ret []string
    109 	t.idleMu.Lock()
    110 	defer t.idleMu.Unlock()
    111 	for _, conns := range t.idleConn {
    112 		for _, pc := range conns {
    113 			ret = append(ret, pc.conn.LocalAddr().String()+"/"+pc.conn.RemoteAddr().String())
    114 		}
    115 	}
    116 	sort.Strings(ret)
    117 	return ret
    118 }
    119 
    120 func (t *Transport) IdleConnStrsForTesting_h2() []string {
    121 	var ret []string
    122 	noDialPool := t.h2transport.ConnPool.(http2noDialClientConnPool)
    123 	pool := noDialPool.http2clientConnPool
    124 
    125 	pool.mu.Lock()
    126 	defer pool.mu.Unlock()
    127 
    128 	for k, cc := range pool.conns {
    129 		for range cc {
    130 			ret = append(ret, k)
    131 		}
    132 	}
    133 
    134 	sort.Strings(ret)
    135 	return ret
    136 }
    137 
    138 func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
    139 	t.idleMu.Lock()
    140 	defer t.idleMu.Unlock()
    141 	for k, conns := range t.idleConn {
    142 		if k.String() == cacheKey {
    143 			return len(conns)
    144 		}
    145 	}
    146 	return 0
    147 }
    148 
    149 func (t *Transport) IdleConnChMapSizeForTesting() int {
    150 	t.idleMu.Lock()
    151 	defer t.idleMu.Unlock()
    152 	return len(t.idleConnCh)
    153 }
    154 
    155 func (t *Transport) IsIdleForTesting() bool {
    156 	t.idleMu.Lock()
    157 	defer t.idleMu.Unlock()
    158 	return t.wantIdle
    159 }
    160 
    161 func (t *Transport) RequestIdleConnChForTesting() {
    162 	t.getIdleConnCh(connectMethod{nil, "http", "example.com"})
    163 }
    164 
    165 func (t *Transport) PutIdleTestConn() bool {
    166 	c, _ := net.Pipe()
    167 	return t.tryPutIdleConn(&persistConn{
    168 		t:        t,
    169 		conn:     c,                   // dummy
    170 		closech:  make(chan struct{}), // so it can be closed
    171 		cacheKey: connectMethodKey{"", "http", "example.com"},
    172 	}) == nil
    173 }
    174 
    175 // All test hooks must be non-nil so they can be called directly,
    176 // but the tests use nil to mean hook disabled.
    177 func unnilTestHook(f *func()) {
    178 	if *f == nil {
    179 		*f = nop
    180 	}
    181 }
    182 
    183 func hookSetter(dst *func()) func(func()) {
    184 	return func(fn func()) {
    185 		unnilTestHook(&fn)
    186 		*dst = fn
    187 	}
    188 }
    189 
    190 func ExportHttp2ConfigureTransport(t *Transport) error {
    191 	t2, err := http2configureTransport(t)
    192 	if err != nil {
    193 		return err
    194 	}
    195 	t.h2transport = t2
    196 	return nil
    197 }
    198 
    199 func (s *Server) ExportAllConnsIdle() bool {
    200 	s.mu.Lock()
    201 	defer s.mu.Unlock()
    202 	for c := range s.activeConn {
    203 		st, ok := c.curState.Load().(ConnState)
    204 		if !ok || st != StateIdle {
    205 			return false
    206 		}
    207 	}
    208 	return true
    209 }
    210 
    211 func (r *Request) WithT(t *testing.T) *Request {
    212 	return r.WithContext(context.WithValue(r.Context(), tLogKey{}, t.Logf))
    213 }
    214 
    215 func ExportSetH2GoawayTimeout(d time.Duration) (restore func()) {
    216 	old := http2goAwayTimeout
    217 	http2goAwayTimeout = d
    218 	return func() { http2goAwayTimeout = old }
    219 }
    220