import (
"net"
- "net/url"
"sync"
"time"
)
+var (
+ DefaultUserAgent = defaultUserAgent
+ NewLoggingConn = newLoggingConn
+ ExportAppendTime = appendTime
+ ExportRefererForURL = refererForURL
+ ExportServerNewConn = (*Server).newConn
+ ExportCloseWriteAndWait = (*conn).closeWriteAndWait
+ ExportErrRequestCanceled = errRequestCanceled
+ ExportServeFile = serveFile
+ ExportHttp2ConfigureTransport = http2ConfigureTransport
+ ExportHttp2ConfigureServer = http2ConfigureServer
+)
+
func init() {
// We only want to pay for this cost during testing.
// When not under test, these values are always nil
testHookMu = new(sync.Mutex)
}
-func NewLoggingConn(baseName string, c net.Conn) net.Conn {
- return newLoggingConn(baseName, c)
+var (
+ SetInstallConnClosedHook = hookSetter(&testHookPersistConnClosedGotRes)
+ SetEnterRoundTripHook = hookSetter(&testHookEnterRoundTrip)
+ SetTestHookWaitResLoop = hookSetter(&testHookWaitResLoop)
+ SetRoundTripRetried = hookSetter(&testHookRoundTripRetried)
+)
+
+func SetReadLoopBeforeNextReadHook(f func()) {
+ testHookMu.Lock()
+ defer testHookMu.Unlock()
+ unnilTestHook(&f)
+ testHookReadLoopBeforeNextRead = f
+}
+
+// SetPendingDialHooks sets the hooks that run before and after handling
+// pending dials.
+func SetPendingDialHooks(before, after func()) {
+ unnilTestHook(&before)
+ unnilTestHook(&after)
+ testHookPrePendingDial, testHookPostPendingDial = before, after
+}
+
+func SetTestHookServerServe(fn func(*Server, net.Listener)) { testHookServerServe = fn }
+
+func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
+ f := func() <-chan time.Time {
+ return ch
+ }
+ return &timeoutHandler{handler, f, ""}
}
-var ExportAppendTime = appendTime
+func ResetCachedEnvironment() {
+ httpProxyEnv.reset()
+ httpsProxyEnv.reset()
+ noProxyEnv.reset()
+}
func (t *Transport) NumPendingRequestsForTesting() int {
t.reqMu.Lock()
})
}
-func SetInstallConnClosedHook(f func()) {
- testHookPersistConnClosedGotRes = f
-}
-
-func SetEnterRoundTripHook(f func()) {
- testHookEnterRoundTrip = f
-}
-
-func SetReadLoopBeforeNextReadHook(f func()) {
- testHookMu.Lock()
- defer testHookMu.Unlock()
- testHookReadLoopBeforeNextRead = f
-}
-
-func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
- f := func() <-chan time.Time {
- return ch
+// All test hooks must be non-nil so they can be called directly,
+// but the tests use nil to mean hook disabled.
+func unnilTestHook(f *func()) {
+ if *f == nil {
+ *f = nop
}
- return &timeoutHandler{handler, f, ""}
-}
-
-func ResetCachedEnvironment() {
- httpProxyEnv.reset()
- httpsProxyEnv.reset()
- noProxyEnv.reset()
-}
-
-var DefaultUserAgent = defaultUserAgent
-
-func ExportRefererForURL(lastReq, newReq *url.URL) string {
- return refererForURL(lastReq, newReq)
-}
-
-// SetPendingDialHooks sets the hooks that run before and after handling
-// pending dials.
-func SetPendingDialHooks(before, after func()) {
- prePendingDial, postPendingDial = before, after
}
-// SetRetriedHook sets the hook that runs when an idempotent retry occurs.
-func SetRetriedHook(hook func()) {
- retried = hook
+func hookSetter(dst *func()) func(func()) {
+ return func(fn func()) {
+ unnilTestHook(&fn)
+ *dst = fn
+ }
}
-
-var ExportServerNewConn = (*Server).newConn
-
-var ExportCloseWriteAndWait = (*conn).closeWriteAndWait
-
-var ExportErrRequestCanceled = errRequestCanceled
-
-var ExportServeFile = serveFile
-
-var ExportHttp2ConfigureTransport = http2ConfigureTransport
-
-var ExportHttp2ConfigureServer = http2ConfigureServer
-
-func SetTestHookServerServe(fn func(*Server, net.Listener)) { testHookServerServe = fn }
if err := checkTransportResend(err, req, pconn); err != nil {
return nil, err
}
- if retried != nil {
- retried()
- }
+ testHookRoundTripRetried()
}
}
return net.Dial(network, addr)
}
-// Testing hooks:
-var prePendingDial, postPendingDial, retried func()
-
// getConn dials and creates a new persistConn to the target as
// specified in the connectMethod. This includes doing a proxy CONNECT
// and/or setting up TLS. If this doesn't return an error, the persistConn
// Copy these hooks so we don't race on the postPendingDial in
// the goroutine we launch. Issue 11136.
- prePendingDial := prePendingDial
- postPendingDial := postPendingDial
+ testHookPrePendingDial := testHookPrePendingDial
+ testHookPostPendingDial := testHookPostPendingDial
handlePendingDial := func() {
- if prePendingDial != nil {
- prePendingDial()
- }
+ testHookPrePendingDial()
go func() {
if v := <-dialc; v.err == nil {
t.putIdleConn(v.pc)
}
- if postPendingDial != nil {
- postPendingDial()
- }
+ testHookPostPendingDial()
}()
}
pc.wroteRequest() &&
pc.t.putIdleConn(pc)
}
-
- if hook := testHookReadLoopBeforeNextRead; hook != nil {
- hook()
- }
+ testHookReadLoopBeforeNextRead()
}
pc.close()
}
var errClosed error = &httpError{err: "net/http: transport closed before response was received"}
var errRequestCanceled = errors.New("net/http: request canceled")
-// nil except for tests
+func nop() {}
+
+// testHooks. Always non-nil.
var (
- testHookPersistConnClosedGotRes func()
- testHookEnterRoundTrip func()
- testHookMu sync.Locker = fakeLocker{} // guards following
- testHookReadLoopBeforeNextRead func()
+ testHookPersistConnClosedGotRes = nop
+ testHookEnterRoundTrip = nop
+ testHookWaitResLoop = nop
+ testHookRoundTripRetried = nop
+ testHookPrePendingDial = nop
+ testHookPostPendingDial = nop
+
+ testHookMu sync.Locker = fakeLocker{} // guards following
+ testHookReadLoopBeforeNextRead = nop
)
// beforeRespHeaderError is used to indicate when an IO error has occurred before
}
func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
- if hook := testHookEnterRoundTrip; hook != nil {
- hook()
- }
+ testHookEnterRoundTrip()
if !pc.t.replaceReqCanceler(req.Request, pc.cancelRequest) {
pc.t.putIdleConn(pc)
return nil, errRequestCanceled
cancelChan := req.Request.Cancel
WaitResponse:
for {
+ testHookWaitResLoop()
select {
case err := <-writeErrCh:
if isNetWriteError(err) {
// with a non-blocking receive.
select {
case re = <-resc:
- if fn := testHookPersistConnClosedGotRes; fn != nil {
- fn()
- }
+ testHookPersistConnClosedGotRes()
default:
re = responseAndError{err: beforeRespHeaderError{errClosed}}
if pc.isCanceled() {