github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/network/transport/http/http_proxy.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); 2 // you may not use this file except in compliance with the License. 3 // You may obtain a copy of the License at 4 // 5 // https://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, 9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 // See the License for the specific language governing permissions and 11 // limitations under the License. 12 // 13 // Original source: github.com/micro/go-micro/v3/network/transport/http/http_proxy.go 14 15 package http 16 17 import ( 18 "bufio" 19 "encoding/base64" 20 "fmt" 21 "io" 22 "net" 23 "net/http" 24 "net/http/httputil" 25 "net/url" 26 ) 27 28 const ( 29 proxyAuthHeader = "Proxy-Authorization" 30 ) 31 32 func getURL(addr string) (*url.URL, error) { 33 r := &http.Request{ 34 URL: &url.URL{ 35 Scheme: "https", 36 Host: addr, 37 }, 38 } 39 return http.ProxyFromEnvironment(r) 40 } 41 42 type pbuffer struct { 43 net.Conn 44 r io.Reader 45 } 46 47 func (p *pbuffer) Read(b []byte) (int, error) { 48 return p.r.Read(b) 49 } 50 51 func proxyDial(conn net.Conn, addr string, proxyURL *url.URL) (_ net.Conn, err error) { 52 defer func() { 53 if err != nil { 54 conn.Close() 55 } 56 }() 57 58 r := &http.Request{ 59 Method: http.MethodConnect, 60 URL: &url.URL{Host: addr}, 61 Header: map[string][]string{"User-Agent": {"micro/latest"}}, 62 } 63 64 if user := proxyURL.User; user != nil { 65 u := user.Username() 66 p, _ := user.Password() 67 auth := []byte(u + ":" + p) 68 basicAuth := base64.StdEncoding.EncodeToString(auth) 69 r.Header.Add(proxyAuthHeader, "Basic "+basicAuth) 70 } 71 72 if err := r.Write(conn); err != nil { 73 return nil, fmt.Errorf("failed to write the HTTP request: %v", err) 74 } 75 76 br := bufio.NewReader(conn) 77 rsp, err := http.ReadResponse(br, r) 78 if err != nil { 79 return nil, fmt.Errorf("reading server HTTP response: %v", err) 80 } 81 defer rsp.Body.Close() 82 if rsp.StatusCode != http.StatusOK { 83 dump, err := httputil.DumpResponse(rsp, true) 84 if err != nil { 85 return nil, fmt.Errorf("failed to do connect handshake, status code: %s", rsp.Status) 86 } 87 return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump) 88 } 89 90 return &pbuffer{Conn: conn, r: br}, nil 91 } 92 93 // Creates a new connection 94 func newConn(dial func(string) (net.Conn, error)) func(string) (net.Conn, error) { 95 return func(addr string) (net.Conn, error) { 96 // get the proxy url 97 proxyURL, err := getURL(addr) 98 if err != nil { 99 return nil, err 100 } 101 102 // set to addr 103 callAddr := addr 104 105 // got proxy 106 if proxyURL != nil { 107 callAddr = proxyURL.Host 108 } 109 110 // dial the addr 111 c, err := dial(callAddr) 112 if err != nil { 113 return nil, err 114 } 115 116 // do proxy connect if we have proxy url 117 if proxyURL != nil { 118 c, err = proxyDial(c, addr, proxyURL) 119 } 120 121 return c, err 122 } 123 }