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