// 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 }