From e17d3afcadcc2ac9abf0d4bf168338a0cdce73fc Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Sat, 22 Jul 2023 13:26:24 +0200 Subject: [PATCH] net/http: clear reference to the request context after transport getConn Clears wannConn ctx and prevents pending dialConnFor after connection delivered or canceled. Fixes #50798 --- src/net/http/transport.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/net/http/transport.go b/src/net/http/transport.go index c07352b018e1ef..114c7585ea9a48 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -1205,7 +1205,6 @@ func (t *Transport) dial(ctx context.Context, network, addr string) (net.Conn, e type wantConn struct { cm connectMethod key connectMethodKey // cm.key() - ctx context.Context // context for dial ready chan struct{} // closed when pc, err pair is delivered // hooks for testing to know when dials are done @@ -1214,7 +1213,8 @@ type wantConn struct { beforeDial func() afterDial func() - mu sync.Mutex // protects pc, err, close(ready) + mu sync.Mutex // protects ctx, pc, err, close(ready) + ctx context.Context // context for dial, cleared after delivered or canceled pc *persistConn err error } @@ -1229,6 +1229,13 @@ func (w *wantConn) waiting() bool { } } +// getCtxForDial returns context for dial or nil if connection was delivered or canceled. +func (w *wantConn) getCtxForDial() context.Context { + w.mu.Lock() + defer w.mu.Unlock() + return w.ctx +} + // tryDeliver attempts to deliver pc, err to w and reports whether it succeeded. func (w *wantConn) tryDeliver(pc *persistConn, err error) bool { w.mu.Lock() @@ -1238,6 +1245,7 @@ func (w *wantConn) tryDeliver(pc *persistConn, err error) bool { return false } + w.ctx = nil w.pc = pc w.err = err if w.pc == nil && w.err == nil { @@ -1255,6 +1263,7 @@ func (w *wantConn) cancel(t *Transport, err error) { close(w.ready) // catch misbehavior in future delivery } pc := w.pc + w.ctx = nil w.pc = nil w.err = err w.mu.Unlock() @@ -1463,8 +1472,12 @@ func (t *Transport) queueForDial(w *wantConn) { // If the dial is canceled or unsuccessful, dialConnFor decrements t.connCount[w.cm.key()]. func (t *Transport) dialConnFor(w *wantConn) { defer w.afterDial() + ctx := w.getCtxForDial() + if ctx == nil { + return + } - pc, err := t.dialConn(w.ctx, w.cm) + pc, err := t.dialConn(ctx, w.cm) delivered := w.tryDeliver(pc, err) if err == nil && (!delivered || pc.alt != nil) { // pconn was not passed to w,