Add support for http proxy
This commit is contained in:
		
							
								
								
									
										109
									
								
								transport/http_proxy.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								transport/http_proxy.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  | package transport | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"net" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httputil" | ||||||
|  | 	"net/url" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	proxyAuthHeader = "Proxy-Authorization" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func getURL(addr string) (*url.URL, error) { | ||||||
|  | 	r := &http.Request{ | ||||||
|  | 		URL: &url.URL{ | ||||||
|  | 			Scheme: "https", | ||||||
|  | 			Host:   addr, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	return http.ProxyFromEnvironment(r) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type pbuffer struct { | ||||||
|  | 	net.Conn | ||||||
|  | 	r io.Reader | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *pbuffer) Read(b []byte) (int, error) { | ||||||
|  | 	return p.r.Read(b) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func proxyDial(conn net.Conn, addr string, proxyURL *url.URL) (_ net.Conn, err error) { | ||||||
|  | 	defer func() { | ||||||
|  | 		if err != nil { | ||||||
|  | 			conn.Close() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	r := &http.Request{ | ||||||
|  | 		Method: http.MethodConnect, | ||||||
|  | 		URL:    &url.URL{Host: addr}, | ||||||
|  | 		Header: map[string][]string{"User-Agent": {"micro/latest"}}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if user := proxyURL.User; user != nil { | ||||||
|  | 		u := user.Username() | ||||||
|  | 		p, _ := user.Password() | ||||||
|  | 		auth := []byte(u + ":" + p) | ||||||
|  | 		basicAuth := base64.StdEncoding.EncodeToString(auth) | ||||||
|  | 		r.Header.Add(proxyAuthHeader, "Basic "+basicAuth) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := r.Write(conn); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to write the HTTP request: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	br := bufio.NewReader(conn) | ||||||
|  | 	rsp, err := http.ReadResponse(br, r) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("reading server HTTP response: %v", err) | ||||||
|  | 	} | ||||||
|  | 	defer rsp.Body.Close() | ||||||
|  | 	if rsp.StatusCode != http.StatusOK { | ||||||
|  | 		dump, err := httputil.DumpResponse(rsp, true) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("failed to do connect handshake, status code: %s", rsp.Status) | ||||||
|  | 		} | ||||||
|  | 		return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &pbuffer{Conn: conn, r: br}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Creates a new connection | ||||||
|  | func newConn(dial func(string) (net.Conn, error)) func(string) (net.Conn, error) { | ||||||
|  | 	return func(addr string) (net.Conn, error) { | ||||||
|  | 		// get the proxy url | ||||||
|  | 		proxyURL, err := getURL(addr) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// set to addr | ||||||
|  | 		callAddr := addr | ||||||
|  |  | ||||||
|  | 		// got proxy | ||||||
|  | 		if proxyURL != nil { | ||||||
|  | 			callAddr = proxyURL.Host | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// dial the addr | ||||||
|  | 		c, err := dial(callAddr) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// do proxy connect if we have proxy url | ||||||
|  | 		if proxyURL != nil { | ||||||
|  | 			c, err = proxyDial(c, addr, proxyURL) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return c, err | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -452,9 +452,13 @@ func (h *httpTransport) Dial(addr string, opts ...DialOption) (Client, error) { | |||||||
| 				InsecureSkipVerify: true, | 				InsecureSkipVerify: true, | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		conn, err = tls.DialWithDialer(&net.Dialer{Timeout: dopts.Timeout}, "tcp", addr, config) | 		conn, err = newConn(func(addr string) (net.Conn, error) { | ||||||
|  | 			return tls.DialWithDialer(&net.Dialer{Timeout: dopts.Timeout}, "tcp", addr, config) | ||||||
|  | 		})(addr) | ||||||
| 	} else { | 	} else { | ||||||
| 		conn, err = net.DialTimeout("tcp", addr, dopts.Timeout) | 		conn, err = newConn(func(addr string) (net.Conn, error) { | ||||||
|  | 			return net.DialTimeout("tcp", addr, dopts.Timeout) | ||||||
|  | 		})(addr) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user