golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/proxy/dial.go (about) 1 // Copyright 2019 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 package proxy 6 7 import ( 8 "context" 9 "net" 10 ) 11 12 // A ContextDialer dials using a context. 13 type ContextDialer interface { 14 DialContext(ctx context.Context, network, address string) (net.Conn, error) 15 } 16 17 // Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment. 18 // 19 // The passed ctx is only used for returning the Conn, not the lifetime of the Conn. 20 // 21 // Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer 22 // can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout. 23 // 24 // A Conn returned from a successful Dial after the context has been cancelled will be immediately closed. 25 func Dial(ctx context.Context, network, address string) (net.Conn, error) { 26 d := FromEnvironment() 27 if xd, ok := d.(ContextDialer); ok { 28 return xd.DialContext(ctx, network, address) 29 } 30 return dialContext(ctx, d, network, address) 31 } 32 33 // WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout 34 // A Conn returned from a successful Dial after the context has been cancelled will be immediately closed. 35 func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) { 36 var ( 37 conn net.Conn 38 done = make(chan struct{}, 1) 39 err error 40 ) 41 go func() { 42 conn, err = d.Dial(network, address) 43 close(done) 44 if conn != nil && ctx.Err() != nil { 45 conn.Close() 46 } 47 }() 48 select { 49 case <-ctx.Done(): 50 err = ctx.Err() 51 case <-done: 52 } 53 return conn, err 54 }