github.com/annwntech/go-micro/v2@v2.9.5/transport/http_proxy.go (about)

     1  package transport
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"net/http"
    10  	"net/http/httputil"
    11  	"net/url"
    12  )
    13  
    14  const (
    15  	proxyAuthHeader = "Proxy-Authorization"
    16  )
    17  
    18  func getURL(addr string) (*url.URL, error) {
    19  	r := &http.Request{
    20  		URL: &url.URL{
    21  			Scheme: "https",
    22  			Host:   addr,
    23  		},
    24  	}
    25  	return http.ProxyFromEnvironment(r)
    26  }
    27  
    28  type pbuffer struct {
    29  	net.Conn
    30  	r io.Reader
    31  }
    32  
    33  func (p *pbuffer) Read(b []byte) (int, error) {
    34  	return p.r.Read(b)
    35  }
    36  
    37  func proxyDial(conn net.Conn, addr string, proxyURL *url.URL) (_ net.Conn, err error) {
    38  	defer func() {
    39  		if err != nil {
    40  			conn.Close()
    41  		}
    42  	}()
    43  
    44  	r := &http.Request{
    45  		Method: http.MethodConnect,
    46  		URL:    &url.URL{Host: addr},
    47  		Header: map[string][]string{"User-Agent": {"micro/latest"}},
    48  	}
    49  
    50  	if user := proxyURL.User; user != nil {
    51  		u := user.Username()
    52  		p, _ := user.Password()
    53  		auth := []byte(u + ":" + p)
    54  		basicAuth := base64.StdEncoding.EncodeToString(auth)
    55  		r.Header.Add(proxyAuthHeader, "Basic "+basicAuth)
    56  	}
    57  
    58  	if err := r.Write(conn); err != nil {
    59  		return nil, fmt.Errorf("failed to write the HTTP request: %v", err)
    60  	}
    61  
    62  	br := bufio.NewReader(conn)
    63  	rsp, err := http.ReadResponse(br, r)
    64  	if err != nil {
    65  		return nil, fmt.Errorf("reading server HTTP response: %v", err)
    66  	}
    67  	defer rsp.Body.Close()
    68  	if rsp.StatusCode != http.StatusOK {
    69  		dump, err := httputil.DumpResponse(rsp, true)
    70  		if err != nil {
    71  			return nil, fmt.Errorf("failed to do connect handshake, status code: %s", rsp.Status)
    72  		}
    73  		return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump)
    74  	}
    75  
    76  	return &pbuffer{Conn: conn, r: br}, nil
    77  }
    78  
    79  // Creates a new connection
    80  func newConn(dial func(string) (net.Conn, error)) func(string) (net.Conn, error) {
    81  	return func(addr string) (net.Conn, error) {
    82  		// get the proxy url
    83  		proxyURL, err := getURL(addr)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  
    88  		// set to addr
    89  		callAddr := addr
    90  
    91  		// got proxy
    92  		if proxyURL != nil {
    93  			callAddr = proxyURL.Host
    94  		}
    95  
    96  		// dial the addr
    97  		c, err := dial(callAddr)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  
   102  		// do proxy connect if we have proxy url
   103  		if proxyURL != nil {
   104  			c, err = proxyDial(c, addr, proxyURL)
   105  		}
   106  
   107  		return c, err
   108  	}
   109  }