github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/netx/proxy.go (about)

     1  package netx
     2  
     3  import (
     4  	"bufio"
     5  	"net"
     6  	"net/http"
     7  	"net/url"
     8  
     9  	"github.com/juju/errors"
    10  	"golang.org/x/net/proxy"
    11  )
    12  
    13  // NewProxyDialer based on proxy url.
    14  func NewProxyDialer(proxyUrl string) (proxy.Dialer, error) {
    15  	u, err := url.Parse(proxyUrl)
    16  	if err != nil {
    17  		return nil, err
    18  	}
    19  
    20  	switch u.Scheme {
    21  	case "socks5":
    22  		return &socks5ProxyClient{proxyUrl: u}, nil
    23  	case "http":
    24  		return &httpProxyClient{proxyUrl: u}, nil
    25  	default:
    26  		return &defaultProxyClient{}, nil
    27  	}
    28  }
    29  
    30  // defaultProxyClient is used to implement a proxy in default.
    31  type defaultProxyClient struct {
    32  	rAddr *net.TCPAddr
    33  }
    34  
    35  // Dial implementation of ProxyConn.
    36  // Set KeepAlive=-1 to reduce the call of syscall.
    37  func (dc *defaultProxyClient) Dial(network string, address string) (conn net.Conn, err error) {
    38  	if dc.rAddr == nil {
    39  		if dc.rAddr, err = net.ResolveTCPAddr("tcp", address); err != nil {
    40  			return nil, err
    41  		}
    42  	}
    43  	return net.DialTCP(network, nil, dc.rAddr)
    44  }
    45  
    46  // socks5ProxyClient is used to implement a proxy in socks5
    47  type socks5ProxyClient struct {
    48  	proxyUrl *url.URL
    49  }
    50  
    51  // Dial implementation of ProxyConn.
    52  func (s5 *socks5ProxyClient) Dial(network string, address string) (net.Conn, error) {
    53  	d, err := proxy.FromURL(s5.proxyUrl, nil)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	return d.Dial(network, address)
    59  }
    60  
    61  // httpProxyClient is used to implement a proxy in http.
    62  type httpProxyClient struct {
    63  	proxyUrl *url.URL
    64  }
    65  
    66  // Dial implementation of ProxyConn
    67  func (hc *httpProxyClient) Dial(_ string, address string) (net.Conn, error) {
    68  	req, err := http.NewRequest("CONNECT", "http://"+address, nil)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	password, _ := hc.proxyUrl.User.Password()
    73  	req.SetBasicAuth(hc.proxyUrl.User.Username(), password)
    74  	proxyConn, err := net.Dial("tcp", hc.proxyUrl.Host)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	if err := req.Write(proxyConn); err != nil {
    79  		return nil, err
    80  	}
    81  	res, err := http.ReadResponse(bufio.NewReader(proxyConn), req)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	_ = res.Body.Close()
    86  	if res.StatusCode != 200 {
    87  		return nil, errors.New("Proxy error " + res.Status)
    88  	}
    89  	return proxyConn, nil
    90  }