diff options
Diffstat (limited to 'src/net/http')
| -rw-r--r-- | src/net/http/h2_bundle.go | 299 |
1 files changed, 95 insertions, 204 deletions
diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index a57d613134..3abd7476f0 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -13,10 +13,6 @@ // // See https://http2.github.io/ for more information on HTTP/2. // -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// package http @@ -1079,7 +1075,7 @@ func http2configFromServer(h1 *Server, h2 *http2Server) http2http2Config { PermitProhibitedCipherSuites: h2.PermitProhibitedCipherSuites, CountError: h2.CountError, } - http2fillNetHTTPServerConfig(&conf, h1) + http2fillNetHTTPConfig(&conf, h1.HTTP2) http2setConfigDefaults(&conf, true) return conf } @@ -1105,7 +1101,7 @@ func http2configFromTransport(h2 *http2Transport) http2http2Config { } if h2.t1 != nil { - http2fillNetHTTPTransportConfig(&conf, h2.t1) + http2fillNetHTTPConfig(&conf, h2.t1.HTTP2) } http2setConfigDefaults(&conf, false) return conf @@ -1145,16 +1141,6 @@ func http2adjustHTTP1MaxHeaderSize(n int64) int64 { return n + typicalHeaders*perFieldOverhead } -// fillNetHTTPServerConfig sets fields in conf from srv.HTTP2. -func http2fillNetHTTPServerConfig(conf *http2http2Config, srv *Server) { - http2fillNetHTTPConfig(conf, srv.HTTP2) -} - -// fillNetHTTPTransportConfig sets fields in conf from tr.HTTP2. -func http2fillNetHTTPTransportConfig(conf *http2http2Config, tr *Transport) { - http2fillNetHTTPConfig(conf, tr.HTTP2) -} - func http2fillNetHTTPConfig(conf *http2http2Config, h2 *HTTP2Config) { if h2 == nil { return @@ -3273,17 +3259,27 @@ func http2summarizeFrame(f http2Frame) string { var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" +// Setting DebugGoroutines to false during a test to disable goroutine debugging +// results in race detector complaints when a test leaves goroutines running before +// returning. Tests shouldn't do this, of course, but when they do it generally shows +// up as infrequent, hard-to-debug flakes. (See #66519.) +// +// Disable goroutine debugging during individual tests with an atomic bool. +// (Note that it's safe to enable/disable debugging mid-test, so the actual race condition +// here is harmless.) +var http2disableDebugGoroutines atomic.Bool + type http2goroutineLock uint64 func http2newGoroutineLock() http2goroutineLock { - if !http2DebugGoroutines { + if !http2DebugGoroutines || http2disableDebugGoroutines.Load() { return 0 } return http2goroutineLock(http2curGoroutineID()) } func (g http2goroutineLock) check() { - if !http2DebugGoroutines { + if !http2DebugGoroutines || http2disableDebugGoroutines.Load() { return } if http2curGoroutineID() != uint64(g) { @@ -3292,7 +3288,7 @@ func (g http2goroutineLock) check() { } func (g http2goroutineLock) checkNotOn() { - if !http2DebugGoroutines { + if !http2DebugGoroutines || http2disableDebugGoroutines.Load() { return } if http2curGoroutineID() == uint64(g) { @@ -3647,15 +3643,13 @@ func (cw http2closeWaiter) Wait() { // idle memory usage with many connections. type http2bufferedWriter struct { _ http2incomparable - group http2synctestGroupInterface // immutable - conn net.Conn // immutable - bw *bufio.Writer // non-nil when data is buffered - byteTimeout time.Duration // immutable, WriteByteTimeout + conn net.Conn // immutable + bw *bufio.Writer // non-nil when data is buffered + byteTimeout time.Duration // immutable, WriteByteTimeout } -func http2newBufferedWriter(group http2synctestGroupInterface, conn net.Conn, timeout time.Duration) *http2bufferedWriter { +func http2newBufferedWriter(conn net.Conn, timeout time.Duration) *http2bufferedWriter { return &http2bufferedWriter{ - group: group, conn: conn, byteTimeout: timeout, } @@ -3706,24 +3700,18 @@ func (w *http2bufferedWriter) Flush() error { type http2bufferedWriterTimeoutWriter http2bufferedWriter func (w *http2bufferedWriterTimeoutWriter) Write(p []byte) (n int, err error) { - return http2writeWithByteTimeout(w.group, w.conn, w.byteTimeout, p) + return http2writeWithByteTimeout(w.conn, w.byteTimeout, p) } // writeWithByteTimeout writes to conn. // If more than timeout passes without any bytes being written to the connection, // the write fails. -func http2writeWithByteTimeout(group http2synctestGroupInterface, conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { +func http2writeWithByteTimeout(conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { if timeout <= 0 { return conn.Write(p) } for { - var now time.Time - if group == nil { - now = time.Now() - } else { - now = group.Now() - } - conn.SetWriteDeadline(now.Add(timeout)) + conn.SetWriteDeadline(time.Now().Add(timeout)) nn, err := conn.Write(p[n:]) n += nn if n == len(p) || nn == 0 || !errors.Is(err, os.ErrDeadlineExceeded) { @@ -3814,17 +3802,6 @@ func (s *http2sorter) SortStrings(ss []string) { // any size (as long as it's first). type http2incomparable [0]func() -// synctestGroupInterface is the methods of synctestGroup used by Server and Transport. -// It's defined as an interface here to let us keep synctestGroup entirely test-only -// and not a part of non-test builds. -type http2synctestGroupInterface interface { - Join() - Now() time.Time - NewTimer(d time.Duration) http2timer - AfterFunc(d time.Duration, f func()) http2timer - ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) -} - // pipe is a goroutine-safe io.Reader/io.Writer pair. It's like // io.Pipe except there are no PipeReader/PipeWriter halves, and the // underlying buffer is an interface. (io.Pipe is always unbuffered) @@ -4121,39 +4098,6 @@ type http2Server struct { // so that we don't embed a Mutex in this struct, which will make the // struct non-copyable, which might break some callers. state *http2serverInternalState - - // Synchronization group used for testing. - // Outside of tests, this is nil. - group http2synctestGroupInterface -} - -func (s *http2Server) markNewGoroutine() { - if s.group != nil { - s.group.Join() - } -} - -func (s *http2Server) now() time.Time { - if s.group != nil { - return s.group.Now() - } - return time.Now() -} - -// newTimer creates a new time.Timer, or a synthetic timer in tests. -func (s *http2Server) newTimer(d time.Duration) http2timer { - if s.group != nil { - return s.group.NewTimer(d) - } - return http2timeTimer{time.NewTimer(d)} -} - -// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. -func (s *http2Server) afterFunc(d time.Duration, f func()) http2timer { - if s.group != nil { - return s.group.AfterFunc(d, f) - } - return http2timeTimer{time.AfterFunc(d, f)} } type http2serverInternalState struct { @@ -4368,6 +4312,9 @@ func (o *http2ServeConnOpts) handler() Handler { // // The opts parameter is optional. If nil, default values are used. func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { + if opts == nil { + opts = &http2ServeConnOpts{} + } s.serveConn(c, opts, nil) } @@ -4383,7 +4330,7 @@ func (s *http2Server) serveConn(c net.Conn, opts *http2ServeConnOpts, newf func( conn: c, baseCtx: baseCtx, remoteAddrStr: c.RemoteAddr().String(), - bw: http2newBufferedWriter(s.group, c, conf.WriteByteTimeout), + bw: http2newBufferedWriter(c, conf.WriteByteTimeout), handler: opts.handler(), streams: make(map[uint32]*http2stream), readFrameCh: make(chan http2readFrameResult), @@ -4583,11 +4530,11 @@ type http2serverConn struct { pingSent bool sentPingData [8]byte goAwayCode http2ErrCode - shutdownTimer http2timer // nil until used - idleTimer http2timer // nil if unused + shutdownTimer *time.Timer // nil until used + idleTimer *time.Timer // nil if unused readIdleTimeout time.Duration pingTimeout time.Duration - readIdleTimer http2timer // nil if unused + readIdleTimer *time.Timer // nil if unused // Owned by the writeFrameAsync goroutine: headerWriteBuf bytes.Buffer @@ -4632,12 +4579,12 @@ type http2stream struct { flow http2outflow // limits writing from Handler to client inflow http2inflow // what the client is allowed to POST/etc to us state http2streamState - resetQueued bool // RST_STREAM queued for write; set by sc.resetStream - gotTrailerHeader bool // HEADER frame for trailers was seen - wroteHeaders bool // whether we wrote headers (not status 100) - readDeadline http2timer // nil if unused - writeDeadline http2timer // nil if unused - closeErr error // set before cw is closed + resetQueued bool // RST_STREAM queued for write; set by sc.resetStream + gotTrailerHeader bool // HEADER frame for trailers was seen + wroteHeaders bool // whether we wrote headers (not status 100) + readDeadline *time.Timer // nil if unused + writeDeadline *time.Timer // nil if unused + closeErr error // set before cw is closed trailer Header // accumulated trailers reqTrailer Header // handler's Request.Trailer @@ -4796,7 +4743,6 @@ type http2readFrameResult struct { // consumer is done with the frame. // It's run on its own goroutine. func (sc *http2serverConn) readFrames() { - sc.srv.markNewGoroutine() gate := make(chan struct{}) gateDone := func() { gate <- struct{}{} } for { @@ -4829,7 +4775,6 @@ type http2frameWriteResult struct { // At most one goroutine can be running writeFrameAsync at a time per // serverConn. func (sc *http2serverConn) writeFrameAsync(wr http2FrameWriteRequest, wd *http2writeData) { - sc.srv.markNewGoroutine() var err error if wd == nil { err = wr.write.writeFrame(sc) @@ -4913,22 +4858,22 @@ func (sc *http2serverConn) serve(conf http2http2Config) { sc.setConnState(StateIdle) if sc.srv.IdleTimeout > 0 { - sc.idleTimer = sc.srv.afterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) + sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) defer sc.idleTimer.Stop() } if conf.SendPingTimeout > 0 { sc.readIdleTimeout = conf.SendPingTimeout - sc.readIdleTimer = sc.srv.afterFunc(conf.SendPingTimeout, sc.onReadIdleTimer) + sc.readIdleTimer = time.AfterFunc(conf.SendPingTimeout, sc.onReadIdleTimer) defer sc.readIdleTimer.Stop() } go sc.readFrames() // closed by defer sc.conn.Close above - settingsTimer := sc.srv.afterFunc(http2firstSettingsTimeout, sc.onSettingsTimer) + settingsTimer := time.AfterFunc(http2firstSettingsTimeout, sc.onSettingsTimer) defer settingsTimer.Stop() - lastFrameTime := sc.srv.now() + lastFrameTime := time.Now() loopNum := 0 for { loopNum++ @@ -4942,7 +4887,7 @@ func (sc *http2serverConn) serve(conf http2http2Config) { case res := <-sc.wroteFrameCh: sc.wroteFrame(res) case res := <-sc.readFrameCh: - lastFrameTime = sc.srv.now() + lastFrameTime = time.Now() // Process any written frames before reading new frames from the client since a // written frame could have triggered a new stream to be started. if sc.writingFrameAsync { @@ -5025,7 +4970,7 @@ func (sc *http2serverConn) handlePingTimer(lastFrameReadTime time.Time) { } pingAt := lastFrameReadTime.Add(sc.readIdleTimeout) - now := sc.srv.now() + now := time.Now() if pingAt.After(now) { // We received frames since arming the ping timer. // Reset it for the next possible timeout. @@ -5092,10 +5037,10 @@ func (sc *http2serverConn) readPreface() error { errc <- nil } }() - timer := sc.srv.newTimer(http2prefaceTimeout) // TODO: configurable on *Server? + timer := time.NewTimer(http2prefaceTimeout) // TODO: configurable on *Server? defer timer.Stop() select { - case <-timer.C(): + case <-timer.C: return http2errPrefaceTimeout case err := <-errc: if err == nil { @@ -5111,6 +5056,21 @@ var http2errChanPool = sync.Pool{ New: func() interface{} { return make(chan error, 1) }, } +func http2getErrChan() chan error { + if http2inTests { + // Channels cannot be reused across synctest tests. + return make(chan error, 1) + } else { + return http2errChanPool.Get().(chan error) + } +} + +func http2putErrChan(ch chan error) { + if !http2inTests { + http2errChanPool.Put(ch) + } +} + var http2writeDataPool = sync.Pool{ New: func() interface{} { return new(http2writeData) }, } @@ -5118,7 +5078,7 @@ var http2writeDataPool = sync.Pool{ // writeDataFromHandler writes DATA response frames from a handler on // the given stream. func (sc *http2serverConn) writeDataFromHandler(stream *http2stream, data []byte, endStream bool) error { - ch := http2errChanPool.Get().(chan error) + ch := http2getErrChan() writeArg := http2writeDataPool.Get().(*http2writeData) *writeArg = http2writeData{stream.id, data, endStream} err := sc.writeFrameFromHandler(http2FrameWriteRequest{ @@ -5150,7 +5110,7 @@ func (sc *http2serverConn) writeDataFromHandler(stream *http2stream, data []byte return http2errStreamClosed } } - http2errChanPool.Put(ch) + http2putErrChan(ch) if frameWriteDone { http2writeDataPool.Put(writeArg) } @@ -5464,7 +5424,7 @@ func (sc *http2serverConn) goAway(code http2ErrCode) { func (sc *http2serverConn) shutDownIn(d time.Duration) { sc.serveG.check() - sc.shutdownTimer = sc.srv.afterFunc(d, sc.onShutdownTimer) + sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer) } func (sc *http2serverConn) resetStream(se http2StreamError) { @@ -6069,7 +6029,7 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { // (in Go 1.8), though. That's a more sane option anyway. if sc.hs.ReadTimeout > 0 { sc.conn.SetReadDeadline(time.Time{}) - st.readDeadline = sc.srv.afterFunc(sc.hs.ReadTimeout, st.onReadTimeout) + st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout) } return sc.scheduleHandler(id, rw, req, handler) @@ -6167,7 +6127,7 @@ func (sc *http2serverConn) newStream(id, pusherID uint32, state http2streamState st.flow.add(sc.initialStreamSendWindowSize) st.inflow.init(sc.initialStreamRecvWindowSize) if sc.hs.WriteTimeout > 0 { - st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) + st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) } sc.streams[id] = st @@ -6356,7 +6316,6 @@ func (sc *http2serverConn) handlerDone() { // Run on its own goroutine. func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, handler func(ResponseWriter, *Request)) { - sc.srv.markNewGoroutine() defer sc.sendServeMsg(http2handlerDoneMsg) didPanic := true defer func() { @@ -6405,7 +6364,7 @@ func (sc *http2serverConn) writeHeaders(st *http2stream, headerData *http2writeR // waiting for this frame to be written, so an http.Flush mid-handler // writes out the correct value of keys, before a handler later potentially // mutates it. - errc = http2errChanPool.Get().(chan error) + errc = http2getErrChan() } if err := sc.writeFrameFromHandler(http2FrameWriteRequest{ write: headerData, @@ -6417,7 +6376,7 @@ func (sc *http2serverConn) writeHeaders(st *http2stream, headerData *http2writeR if errc != nil { select { case err := <-errc: - http2errChanPool.Put(errc) + http2putErrChan(errc) return err case <-sc.doneServing: return http2errClientDisconnected @@ -6524,7 +6483,7 @@ func (b *http2requestBody) Read(p []byte) (n int, err error) { if err == io.EOF { b.sawEOF = true } - if b.conn == nil && http2inTests { + if b.conn == nil { return } b.conn.noteBodyReadFromHandler(b.stream, n, err) @@ -6653,7 +6612,7 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { var date string if _, ok := rws.snapHeader["Date"]; !ok { // TODO(bradfitz): be faster here, like net/http? measure. - date = rws.conn.srv.now().UTC().Format(TimeFormat) + date = time.Now().UTC().Format(TimeFormat) } for _, v := range rws.snapHeader["Trailer"] { @@ -6775,7 +6734,7 @@ func (rws *http2responseWriterState) promoteUndeclaredTrailers() { func (w *http2responseWriter) SetReadDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { + if !deadline.IsZero() && deadline.Before(time.Now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onReadTimeout() @@ -6791,9 +6750,9 @@ func (w *http2responseWriter) SetReadDeadline(deadline time.Time) error { if deadline.IsZero() { st.readDeadline = nil } else if st.readDeadline == nil { - st.readDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onReadTimeout) + st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout) } else { - st.readDeadline.Reset(deadline.Sub(sc.srv.now())) + st.readDeadline.Reset(deadline.Sub(time.Now())) } }) return nil @@ -6801,7 +6760,7 @@ func (w *http2responseWriter) SetReadDeadline(deadline time.Time) error { func (w *http2responseWriter) SetWriteDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { + if !deadline.IsZero() && deadline.Before(time.Now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onWriteTimeout() @@ -6817,9 +6776,9 @@ func (w *http2responseWriter) SetWriteDeadline(deadline time.Time) error { if deadline.IsZero() { st.writeDeadline = nil } else if st.writeDeadline == nil { - st.writeDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onWriteTimeout) + st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout) } else { - st.writeDeadline.Reset(deadline.Sub(sc.srv.now())) + st.writeDeadline.Reset(deadline.Sub(time.Now())) } }) return nil @@ -7098,7 +7057,7 @@ func (w *http2responseWriter) Push(target string, opts *PushOptions) error { method: opts.Method, url: u, header: http2cloneHeader(opts.Header), - done: http2errChanPool.Get().(chan error), + done: http2getErrChan(), } select { @@ -7115,7 +7074,7 @@ func (w *http2responseWriter) Push(target string, opts *PushOptions) error { case <-st.cw: return http2errStreamClosed case err := <-msg.done: - http2errChanPool.Put(msg.done) + http2putErrChan(msg.done) return err } } @@ -7300,20 +7259,6 @@ func (sc *http2serverConn) countError(name string, err error) error { return err } -// A timer is a time.Timer, as an interface which can be replaced in tests. -type http2timer = interface { - C() <-chan time.Time - Reset(d time.Duration) bool - Stop() bool -} - -// timeTimer adapts a time.Timer to the timer interface. -type http2timeTimer struct { - *time.Timer -} - -func (t http2timeTimer) C() <-chan time.Time { return t.Timer.C } - const ( // transportDefaultConnFlow is how many connection-level flow control // tokens we give the server at start-up, past the default 64k. @@ -7470,50 +7415,6 @@ type http2Transport struct { type http2transportTestHooks struct { newclientconn func(*http2ClientConn) - group http2synctestGroupInterface -} - -func (t *http2Transport) markNewGoroutine() { - if t != nil && t.http2transportTestHooks != nil { - t.http2transportTestHooks.group.Join() - } -} - -func (t *http2Transport) now() time.Time { - if t != nil && t.http2transportTestHooks != nil { - return t.http2transportTestHooks.group.Now() - } - return time.Now() -} - -func (t *http2Transport) timeSince(when time.Time) time.Duration { - if t != nil && t.http2transportTestHooks != nil { - return t.now().Sub(when) - } - return time.Since(when) -} - -// newTimer creates a new time.Timer, or a synthetic timer in tests. -func (t *http2Transport) newTimer(d time.Duration) http2timer { - if t.http2transportTestHooks != nil { - return t.http2transportTestHooks.group.NewTimer(d) - } - return http2timeTimer{time.NewTimer(d)} -} - -// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. -func (t *http2Transport) afterFunc(d time.Duration, f func()) http2timer { - if t.http2transportTestHooks != nil { - return t.http2transportTestHooks.group.AfterFunc(d, f) - } - return http2timeTimer{time.AfterFunc(d, f)} -} - -func (t *http2Transport) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { - if t.http2transportTestHooks != nil { - return t.http2transportTestHooks.group.ContextWithTimeout(ctx, d) - } - return context.WithTimeout(ctx, d) } func (t *http2Transport) maxHeaderListSize() uint32 { @@ -7643,7 +7544,7 @@ type http2ClientConn struct { readerErr error // set before readerDone is closed idleTimeout time.Duration // or 0 for never - idleTimer http2timer + idleTimer *time.Timer mu sync.Mutex // guards following cond *sync.Cond // hold mu; broadcast on flow/closed changes @@ -7811,14 +7712,12 @@ func (cs *http2clientStream) closeReqBodyLocked() { cs.reqBodyClosed = make(chan struct{}) reqBodyClosed := cs.reqBodyClosed go func() { - cs.cc.t.markNewGoroutine() cs.reqBody.Close() close(reqBodyClosed) }() } type http2stickyErrWriter struct { - group http2synctestGroupInterface conn net.Conn timeout time.Duration err *error @@ -7828,7 +7727,7 @@ func (sew http2stickyErrWriter) Write(p []byte) (n int, err error) { if *sew.err != nil { return 0, *sew.err } - n, err = http2writeWithByteTimeout(sew.group, sew.conn, sew.timeout, p) + n, err = http2writeWithByteTimeout(sew.conn, sew.timeout, p) *sew.err = err return n, err } @@ -7928,9 +7827,9 @@ func (t *http2Transport) RoundTripOpt(req *Request, opt http2RoundTripOpt) (*Res backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) d := time.Second * time.Duration(backoff) - tm := t.newTimer(d) + tm := time.NewTimer(d) select { - case <-tm.C(): + case <-tm.C: t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) continue case <-req.Context().Done(): @@ -7977,6 +7876,7 @@ var ( http2errClientConnUnusable = errors.New("http2: client conn not usable") http2errClientConnNotEstablished = errors.New("http2: client conn could not be established") http2errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") + http2errClientConnForceClosed = errors.New("http2: client connection force closed via ClientConn.Close") ) // shouldRetryRequest is called by RoundTrip when a request fails to get @@ -8116,14 +8016,11 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client pingTimeout: conf.PingTimeout, pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), - lastActive: t.now(), + lastActive: time.Now(), } - var group http2synctestGroupInterface if t.http2transportTestHooks != nil { - t.markNewGoroutine() t.http2transportTestHooks.newclientconn(cc) c = cc.tconn - group = t.group } if http2VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) @@ -8135,7 +8032,6 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client // TODO: adjust this writer size to account for frame size + // MTU + crypto/tls record padding. cc.bw = bufio.NewWriter(http2stickyErrWriter{ - group: group, conn: c, timeout: conf.WriteByteTimeout, err: &cc.werr, @@ -8184,7 +8080,7 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client // Start the idle timer after the connection is fully initialized. if d := t.idleConnTimeout(); d != 0 { cc.idleTimeout = d - cc.idleTimer = t.afterFunc(d, cc.onIdleTimeout) + cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout) } go cc.readLoop() @@ -8195,7 +8091,7 @@ func (cc *http2ClientConn) healthCheck() { pingTimeout := cc.pingTimeout // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. - ctx, cancel := cc.t.contextWithTimeout(context.Background(), pingTimeout) + ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) defer cancel() cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) @@ -8398,7 +8294,7 @@ func (cc *http2ClientConn) tooIdleLocked() bool { // times are compared based on their wall time. We don't want // to reuse a connection that's been sitting idle during // VM/laptop suspend if monotonic time was also frozen. - return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && cc.t.timeSince(cc.lastIdle.Round(0)) > cc.idleTimeout + return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout } // onIdleTimeout is called from a time.AfterFunc goroutine. It will @@ -8464,7 +8360,6 @@ func (cc *http2ClientConn) Shutdown(ctx context.Context) error { done := make(chan struct{}) cancelled := false // guarded by cc.mu go func() { - cc.t.markNewGoroutine() cc.mu.Lock() defer cc.mu.Unlock() for { @@ -8535,8 +8430,7 @@ func (cc *http2ClientConn) closeForError(err error) { // // In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead. func (cc *http2ClientConn) Close() error { - err := errors.New("http2: client connection force closed via ClientConn.Close") - cc.closeForError(err) + cc.closeForError(http2errClientConnForceClosed) return nil } @@ -8705,7 +8599,6 @@ func (cc *http2ClientConn) roundTrip(req *Request, streamf func(*http2clientStre // // It sends the request and performs post-request cleanup (closing Request.Body, etc.). func (cs *http2clientStream) doRequest(req *Request, streamf func(*http2clientStream)) { - cs.cc.t.markNewGoroutine() err := cs.writeRequest(req, streamf) cs.cleanupWriteRequest(err) } @@ -8836,9 +8729,9 @@ func (cs *http2clientStream) writeRequest(req *Request, streamf func(*http2clien var respHeaderTimer <-chan time.Time var respHeaderRecv chan struct{} if d := cc.responseHeaderTimeout(); d != 0 { - timer := cc.t.newTimer(d) + timer := time.NewTimer(d) defer timer.Stop() - respHeaderTimer = timer.C() + respHeaderTimer = timer.C respHeaderRecv = cs.respHeaderRecv } // Wait until the peer half-closes its end of the stream, @@ -9031,7 +8924,7 @@ func (cc *http2ClientConn) awaitOpenSlotForStreamLocked(cs *http2clientStream) e // Return a fatal error which aborts the retry loop. return http2errClientConnNotEstablished } - cc.lastActive = cc.t.now() + cc.lastActive = time.Now() if cc.closed || !cc.canTakeNewRequestLocked() { return http2errClientConnUnusable } @@ -9371,10 +9264,10 @@ func (cc *http2ClientConn) forgetStreamID(id uint32) { if len(cc.streams) != slen-1 { panic("forgetting unknown stream id") } - cc.lastActive = cc.t.now() + cc.lastActive = time.Now() if len(cc.streams) == 0 && cc.idleTimer != nil { cc.idleTimer.Reset(cc.idleTimeout) - cc.lastIdle = cc.t.now() + cc.lastIdle = time.Now() } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. @@ -9400,7 +9293,6 @@ type http2clientConnReadLoop struct { // readLoop runs in its own goroutine and reads and dispatches frames. func (cc *http2ClientConn) readLoop() { - cc.t.markNewGoroutine() rl := &http2clientConnReadLoop{cc: cc} defer rl.cleanup() cc.readerErr = rl.run() @@ -9467,9 +9359,9 @@ func (rl *http2clientConnReadLoop) cleanup() { if cc.idleTimeout > 0 && unusedWaitTime > cc.idleTimeout { unusedWaitTime = cc.idleTimeout } - idleTime := cc.t.now().Sub(cc.lastActive) + idleTime := time.Now().Sub(cc.lastActive) if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime && !cc.closedOnIdle { - cc.idleTimer = cc.t.afterFunc(unusedWaitTime-idleTime, func() { + cc.idleTimer = time.AfterFunc(unusedWaitTime-idleTime, func() { cc.t.connPool().MarkDead(cc) }) } else { @@ -9529,9 +9421,9 @@ func (rl *http2clientConnReadLoop) run() error { cc := rl.cc gotSettings := false readIdleTimeout := cc.readIdleTimeout - var t http2timer + var t *time.Timer if readIdleTimeout != 0 { - t = cc.t.afterFunc(readIdleTimeout, cc.healthCheck) + t = time.AfterFunc(readIdleTimeout, cc.healthCheck) } for { f, err := cc.fr.ReadFrame() @@ -10277,7 +10169,6 @@ func (cc *http2ClientConn) Ping(ctx context.Context) error { var pingError error errc := make(chan struct{}) go func() { - cc.t.markNewGoroutine() cc.wmu.Lock() defer cc.wmu.Unlock() if pingError = cc.fr.WritePing(false, p); pingError != nil { @@ -10510,7 +10401,7 @@ func http2traceGotConn(req *Request, cc *http2ClientConn, reused bool) { cc.mu.Lock() ci.WasIdle = len(cc.streams) == 0 && reused if ci.WasIdle && !cc.lastActive.IsZero() { - ci.IdleTime = cc.t.timeSince(cc.lastActive) + ci.IdleTime = time.Since(cc.lastActive) } cc.mu.Unlock() |
