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  }