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  }