github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/net/http/export_test.go (about) 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.TB) { 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 unnilTestHook(&f) 76 testHookReadLoopBeforeNextRead = f 77 } 78 79 // SetPendingDialHooks sets the hooks that run before and after handling 80 // pending dials. 81 func SetPendingDialHooks(before, after func()) { 82 unnilTestHook(&before) 83 unnilTestHook(&after) 84 testHookPrePendingDial, testHookPostPendingDial = before, after 85 } 86 87 func SetTestHookServerServe(fn func(*Server, net.Listener)) { testHookServerServe = fn } 88 89 func NewTestTimeoutHandler(handler Handler, ctx context.Context) Handler { 90 return &timeoutHandler{ 91 handler: handler, 92 testContext: ctx, 93 // (no body) 94 } 95 } 96 97 func ResetCachedEnvironment() { 98 resetProxyConfig() 99 } 100 101 func (t *Transport) NumPendingRequestsForTesting() int { 102 t.reqMu.Lock() 103 defer t.reqMu.Unlock() 104 return len(t.reqCanceler) 105 } 106 107 func (t *Transport) IdleConnKeysForTesting() (keys []string) { 108 keys = make([]string, 0) 109 t.idleMu.Lock() 110 defer t.idleMu.Unlock() 111 for key := range t.idleConn { 112 keys = append(keys, key.String()) 113 } 114 sort.Strings(keys) 115 return 116 } 117 118 func (t *Transport) IdleConnKeyCountForTesting() int { 119 t.idleMu.Lock() 120 defer t.idleMu.Unlock() 121 return len(t.idleConn) 122 } 123 124 func (t *Transport) IdleConnStrsForTesting() []string { 125 var ret []string 126 t.idleMu.Lock() 127 defer t.idleMu.Unlock() 128 for _, conns := range t.idleConn { 129 for _, pc := range conns { 130 ret = append(ret, pc.conn.LocalAddr().String()+"/"+pc.conn.RemoteAddr().String()) 131 } 132 } 133 sort.Strings(ret) 134 return ret 135 } 136 137 func (t *Transport) IdleConnStrsForTesting_h2() []string { 138 var ret []string 139 noDialPool := t.h2transport.(*http2Transport).ConnPool.(http2noDialClientConnPool) 140 pool := noDialPool.http2clientConnPool 141 142 pool.mu.Lock() 143 defer pool.mu.Unlock() 144 145 for k, cc := range pool.conns { 146 for range cc { 147 ret = append(ret, k) 148 } 149 } 150 151 sort.Strings(ret) 152 return ret 153 } 154 155 func (t *Transport) IdleConnCountForTesting(scheme, addr string) int { 156 t.idleMu.Lock() 157 defer t.idleMu.Unlock() 158 key := connectMethodKey{"", scheme, addr, false} 159 cacheKey := key.String() 160 for k, conns := range t.idleConn { 161 if k.String() == cacheKey { 162 return len(conns) 163 } 164 } 165 return 0 166 } 167 168 func (t *Transport) IdleConnWaitMapSizeForTesting() int { 169 t.idleMu.Lock() 170 defer t.idleMu.Unlock() 171 return len(t.idleConnWait) 172 } 173 174 func (t *Transport) IsIdleForTesting() bool { 175 t.idleMu.Lock() 176 defer t.idleMu.Unlock() 177 return t.closeIdle 178 } 179 180 func (t *Transport) QueueForIdleConnForTesting() { 181 t.queueForIdleConn(nil) 182 } 183 184 // PutIdleTestConn reports whether it was able to insert a fresh 185 // persistConn for scheme, addr into the idle connection pool. 186 func (t *Transport) PutIdleTestConn(scheme, addr string) bool { 187 c, _ := net.Pipe() 188 key := connectMethodKey{"", scheme, addr, false} 189 190 if t.MaxConnsPerHost > 0 { 191 // Transport is tracking conns-per-host. 192 // Increment connection count to account 193 // for new persistConn created below. 194 t.connsPerHostMu.Lock() 195 if t.connsPerHost == nil { 196 t.connsPerHost = make(map[connectMethodKey]int) 197 } 198 t.connsPerHost[key]++ 199 t.connsPerHostMu.Unlock() 200 } 201 202 return t.tryPutIdleConn(&persistConn{ 203 t: t, 204 conn: c, // dummy 205 closech: make(chan struct{}), // so it can be closed 206 cacheKey: key, 207 }) == nil 208 } 209 210 // PutIdleTestConnH2 reports whether it was able to insert a fresh 211 // HTTP/2 persistConn for scheme, addr into the idle connection pool. 212 func (t *Transport) PutIdleTestConnH2(scheme, addr string, alt RoundTripper) bool { 213 key := connectMethodKey{"", scheme, addr, false} 214 215 if t.MaxConnsPerHost > 0 { 216 // Transport is tracking conns-per-host. 217 // Increment connection count to account 218 // for new persistConn created below. 219 t.connsPerHostMu.Lock() 220 if t.connsPerHost == nil { 221 t.connsPerHost = make(map[connectMethodKey]int) 222 } 223 t.connsPerHost[key]++ 224 t.connsPerHostMu.Unlock() 225 } 226 227 return t.tryPutIdleConn(&persistConn{ 228 t: t, 229 alt: alt, 230 cacheKey: key, 231 }) == nil 232 } 233 234 // All test hooks must be non-nil so they can be called directly, 235 // but the tests use nil to mean hook disabled. 236 func unnilTestHook(f *func()) { 237 if *f == nil { 238 *f = nop 239 } 240 } 241 242 func hookSetter(dst *func()) func(func()) { 243 return func(fn func()) { 244 unnilTestHook(&fn) 245 *dst = fn 246 } 247 } 248 249 func ExportHttp2ConfigureTransport(t *Transport) error { 250 t2, err := http2configureTransports(t) 251 if err != nil { 252 return err 253 } 254 t.h2transport = t2 255 return nil 256 } 257 258 func (s *Server) ExportAllConnsIdle() bool { 259 s.mu.Lock() 260 defer s.mu.Unlock() 261 for c := range s.activeConn { 262 st, unixSec := c.getState() 263 if unixSec == 0 || st != StateIdle { 264 return false 265 } 266 } 267 return true 268 } 269 270 func (s *Server) ExportAllConnsByState() map[ConnState]int { 271 states := map[ConnState]int{} 272 s.mu.Lock() 273 defer s.mu.Unlock() 274 for c := range s.activeConn { 275 st, _ := c.getState() 276 states[st] += 1 277 } 278 return states 279 } 280 281 func (r *Request) WithT(t *testing.T) *Request { 282 return r.WithContext(context.WithValue(r.Context(), tLogKey{}, t.Logf)) 283 } 284 285 func ExportSetH2GoawayTimeout(d time.Duration) (restore func()) { 286 old := http2goAwayTimeout 287 http2goAwayTimeout = d 288 return func() { http2goAwayTimeout = old } 289 } 290 291 func (r *Request) ExportIsReplayable() bool { return r.isReplayable() } 292 293 // ExportCloseTransportConnsAbruptly closes all idle connections from 294 // tr in an abrupt way, just reaching into the underlying Conns and 295 // closing them, without telling the Transport or its persistConns 296 // that it's doing so. This is to simulate the server closing connections 297 // on the Transport. 298 func ExportCloseTransportConnsAbruptly(tr *Transport) { 299 tr.idleMu.Lock() 300 for _, pcs := range tr.idleConn { 301 for _, pc := range pcs { 302 pc.conn.Close() 303 } 304 } 305 tr.idleMu.Unlock() 306 } 307 308 // ResponseWriterConnForTesting returns w's underlying connection, if w 309 // is a regular *response ResponseWriter. 310 func ResponseWriterConnForTesting(w ResponseWriter) (c net.Conn, ok bool) { 311 if r, ok := w.(*response); ok { 312 return r.conn.rwc, true 313 } 314 return nil, false 315 }