github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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 "slices" 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 var 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 slices.Sort(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 slices.Sort(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, ccs := range pool.conns { 146 for _, cc := range ccs { 147 if cc.idleState().canTakeNewRequest { 148 ret = append(ret, k) 149 } 150 } 151 } 152 153 slices.Sort(ret) 154 return ret 155 } 156 157 func (t *Transport) IdleConnCountForTesting(scheme, addr string) int { 158 t.idleMu.Lock() 159 defer t.idleMu.Unlock() 160 key := connectMethodKey{"", scheme, addr, false} 161 cacheKey := key.String() 162 for k, conns := range t.idleConn { 163 if k.String() == cacheKey { 164 return len(conns) 165 } 166 } 167 return 0 168 } 169 170 func (t *Transport) IdleConnWaitMapSizeForTesting() int { 171 t.idleMu.Lock() 172 defer t.idleMu.Unlock() 173 return len(t.idleConnWait) 174 } 175 176 func (t *Transport) IsIdleForTesting() bool { 177 t.idleMu.Lock() 178 defer t.idleMu.Unlock() 179 return t.closeIdle 180 } 181 182 func (t *Transport) QueueForIdleConnForTesting() { 183 t.queueForIdleConn(nil) 184 } 185 186 // PutIdleTestConn reports whether it was able to insert a fresh 187 // persistConn for scheme, addr into the idle connection pool. 188 func (t *Transport) PutIdleTestConn(scheme, addr string) bool { 189 c, _ := net.Pipe() 190 key := connectMethodKey{"", scheme, addr, false} 191 192 if t.MaxConnsPerHost > 0 { 193 // Transport is tracking conns-per-host. 194 // Increment connection count to account 195 // for new persistConn created below. 196 t.connsPerHostMu.Lock() 197 if t.connsPerHost == nil { 198 t.connsPerHost = make(map[connectMethodKey]int) 199 } 200 t.connsPerHost[key]++ 201 t.connsPerHostMu.Unlock() 202 } 203 204 return t.tryPutIdleConn(&persistConn{ 205 t: t, 206 conn: c, // dummy 207 closech: make(chan struct{}), // so it can be closed 208 cacheKey: key, 209 }) == nil 210 } 211 212 // PutIdleTestConnH2 reports whether it was able to insert a fresh 213 // HTTP/2 persistConn for scheme, addr into the idle connection pool. 214 func (t *Transport) PutIdleTestConnH2(scheme, addr string, alt RoundTripper) bool { 215 key := connectMethodKey{"", scheme, addr, false} 216 217 if t.MaxConnsPerHost > 0 { 218 // Transport is tracking conns-per-host. 219 // Increment connection count to account 220 // for new persistConn created below. 221 t.connsPerHostMu.Lock() 222 if t.connsPerHost == nil { 223 t.connsPerHost = make(map[connectMethodKey]int) 224 } 225 t.connsPerHost[key]++ 226 t.connsPerHostMu.Unlock() 227 } 228 229 return t.tryPutIdleConn(&persistConn{ 230 t: t, 231 alt: alt, 232 cacheKey: key, 233 }) == nil 234 } 235 236 // All test hooks must be non-nil so they can be called directly, 237 // but the tests use nil to mean hook disabled. 238 func unnilTestHook(f *func()) { 239 if *f == nil { 240 *f = nop 241 } 242 } 243 244 func hookSetter(dst *func()) func(func()) { 245 return func(fn func()) { 246 unnilTestHook(&fn) 247 *dst = fn 248 } 249 } 250 251 func ExportHttp2ConfigureTransport(t *Transport) error { 252 t2, err := http2configureTransports(t) 253 if err != nil { 254 return err 255 } 256 t.h2transport = t2 257 return nil 258 } 259 260 func (s *Server) ExportAllConnsIdle() bool { 261 s.mu.Lock() 262 defer s.mu.Unlock() 263 for c := range s.activeConn { 264 st, unixSec := c.getState() 265 if unixSec == 0 || st != StateIdle { 266 return false 267 } 268 } 269 return true 270 } 271 272 func (s *Server) ExportAllConnsByState() map[ConnState]int { 273 states := map[ConnState]int{} 274 s.mu.Lock() 275 defer s.mu.Unlock() 276 for c := range s.activeConn { 277 st, _ := c.getState() 278 states[st] += 1 279 } 280 return states 281 } 282 283 func (r *Request) WithT(t *testing.T) *Request { 284 return r.WithContext(context.WithValue(r.Context(), tLogKey{}, t.Logf)) 285 } 286 287 func ExportSetH2GoawayTimeout(d time.Duration) (restore func()) { 288 old := http2goAwayTimeout 289 http2goAwayTimeout = d 290 return func() { http2goAwayTimeout = old } 291 } 292 293 func (r *Request) ExportIsReplayable() bool { return r.isReplayable() } 294 295 // ExportCloseTransportConnsAbruptly closes all idle connections from 296 // tr in an abrupt way, just reaching into the underlying Conns and 297 // closing them, without telling the Transport or its persistConns 298 // that it's doing so. This is to simulate the server closing connections 299 // on the Transport. 300 func ExportCloseTransportConnsAbruptly(tr *Transport) { 301 tr.idleMu.Lock() 302 for _, pcs := range tr.idleConn { 303 for _, pc := range pcs { 304 pc.conn.Close() 305 } 306 } 307 tr.idleMu.Unlock() 308 } 309 310 // ResponseWriterConnForTesting returns w's underlying connection, if w 311 // is a regular *response ResponseWriter. 312 func ResponseWriterConnForTesting(w ResponseWriter) (c net.Conn, ok bool) { 313 if r, ok := w.(*response); ok { 314 return r.conn.rwc, true 315 } 316 return nil, false 317 } 318 319 func init() { 320 // Set the default rstAvoidanceDelay to the minimum possible value to shake 321 // out tests that unexpectedly depend on it. Such tests should use 322 // runTimeSensitiveTest and SetRSTAvoidanceDelay to explicitly raise the delay 323 // if needed. 324 rstAvoidanceDelay = 1 * time.Nanosecond 325 } 326 327 // SetRSTAvoidanceDelay sets how long we are willing to wait between calling 328 // CloseWrite on a connection and fully closing the connection. 329 func SetRSTAvoidanceDelay(t *testing.T, d time.Duration) { 330 prevDelay := rstAvoidanceDelay 331 t.Cleanup(func() { 332 rstAvoidanceDelay = prevDelay 333 }) 334 rstAvoidanceDelay = d 335 }