go-micro.dev/v5@v5.12.0/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 26 return http.ProxyFromEnvironment(r) 27 } 28 29 type pbuffer struct { 30 net.Conn 31 r io.Reader 32 } 33 34 func (p *pbuffer) Read(b []byte) (int, error) { 35 return p.r.Read(b) 36 } 37 38 func proxyDial(conn net.Conn, addr string, proxyURL *url.URL) (_ net.Conn, err error) { 39 defer func() { 40 if err != nil { 41 // trunk-ignore(golangci-lint/errcheck) 42 conn.Close() 43 } 44 }() 45 46 r := &http.Request{ 47 Method: http.MethodConnect, 48 URL: &url.URL{Host: addr}, 49 Header: map[string][]string{"User-Agent": {"micro/latest"}}, 50 } 51 52 if user := proxyURL.User; user != nil { 53 u := user.Username() 54 p, _ := user.Password() 55 auth := []byte(u + ":" + p) 56 basicAuth := base64.StdEncoding.EncodeToString(auth) 57 r.Header.Add(proxyAuthHeader, "Basic "+basicAuth) 58 } 59 60 if err := r.Write(conn); err != nil { 61 return nil, fmt.Errorf("failed to write the HTTP request: %w", err) 62 } 63 64 br := bufio.NewReader(conn) 65 66 rsp, err := http.ReadResponse(br, r) 67 if err != nil { 68 return nil, fmt.Errorf("reading server HTTP response: %w", err) 69 } 70 71 defer func() { 72 err = rsp.Body.Close() 73 }() 74 75 if rsp.StatusCode != http.StatusOK { 76 dump, err := httputil.DumpResponse(rsp, true) 77 if err != nil { 78 return nil, fmt.Errorf("failed to do connect handshake, status code: %s", rsp.Status) 79 } 80 81 return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump) 82 } 83 84 return &pbuffer{Conn: conn, r: br}, nil 85 } 86 87 // Creates a new connection. 88 func newConn(dial func(string) (net.Conn, error)) func(string) (net.Conn, error) { 89 return func(addr string) (net.Conn, error) { 90 // get the proxy url 91 proxyURL, err := getURL(addr) 92 if err != nil { 93 return nil, err 94 } 95 96 // set to addr 97 callAddr := addr 98 99 // got proxy 100 if proxyURL != nil { 101 callAddr = proxyURL.Host 102 } 103 104 // dial the addr 105 c, err := dial(callAddr) 106 if err != nil { 107 return nil, err 108 } 109 110 // do proxy connect if we have proxy url 111 if proxyURL != nil { 112 c, err = proxyDial(c, addr, proxyURL) 113 } 114 115 return c, err 116 } 117 }