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