110 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package http
 | 
						|
 | 
						|
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
 | 
						|
	}
 | 
						|
}
 |