-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
(*http.Transport).getConn currently starts a dialConn call in a background goroutine:
Lines 942 to 945 in 0b0cc41
| go func() { | |
| pc, err := t.dialConn(ctx, cm) | |
| dialc <- dialRes{pc, err} | |
| }() |
That records traces to the provided Context and eventually invokes t.DialContext with it:
Line 1029 in 0b0cc41
| trace := httptrace.ContextClientTrace(ctx) |
Line 1060 in 0b0cc41
| conn, err := t.dial(ctx, "tcp", cm.addr()) |
This is pretty much a textbook illustration of the problem described in #19643 (Context API for continuing work). If (*Transport).getConn returns early (due to cancellation or to availability of an idle connection), the caller may have already written out the corresponding traces, and dialConn (and/or the user-provided DialContext callback) will unexpectedly access a Context that the caller believes to be unreachable.
httptrace.ClientTrace says, "Functions may be called concurrently from different goroutines and some may be called after the request has completed or failed." However, that is not true of Context instances in general: if the http package wants to save a trace after a call has returned, it should call Value ahead of time and save only the ClientTrace pointer. If dialConn calls a user-provided DialContext function, then getConn should cancel the Context passed to it and wait for DialContext to return before itself returning.
See also #20617 (Context race in http.Transport).