55 lines
		
	
	
		
			1.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			55 lines
		
	
	
		
			1.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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
 | 
						|
}
 |