| // Copyright 2019 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 proxy | |
| import ( | |
| "context" | |
| "net" | |
| ) | |
| // A ContextDialer dials using a context. | |
| type ContextDialer interface { | |
| DialContext(ctx context.Context, network, address string) (net.Conn, error) | |
| } | |
| // Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment. | |
| // | |
| // The passed ctx is only used for returning the Conn, not the lifetime of the Conn. | |
| // | |
| // Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer | |
| // can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout. | |
| // | |
| // A Conn returned from a successful Dial after the context has been cancelled will be immediately closed. | |
| func Dial(ctx context.Context, network, address string) (net.Conn, error) { | |
| d := FromEnvironment() | |
| if xd, ok := d.(ContextDialer); ok { | |
| return xd.DialContext(ctx, network, address) | |
| } | |
| return dialContext(ctx, d, network, address) | |
| } | |
| // WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout | |
| // A Conn returned from a successful Dial after the context has been cancelled will be immediately closed. | |
| func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) { | |
| var ( | |
| conn net.Conn | |
| done = make(chan struct{}, 1) | |
| err error | |
| ) | |
| go func() { | |
| conn, err = d.Dial(network, address) | |
| close(done) | |
| if conn != nil && ctx.Err() != nil { | |
| conn.Close() | |
| } | |
| }() | |
| select { | |
| case <-ctx.Done(): | |
| err = ctx.Err() | |
| case <-done: | |
| } | |
| return conn, err | |
| } |