github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/http/client.go (about)

     1  package http
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"encoding/base64"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"net/url"
    11  
    12  	"github.com/Asutorufa/yuhaiin/pkg/net/netapi"
    13  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/point"
    14  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/protocol"
    15  )
    16  
    17  type client struct {
    18  	netapi.Proxy
    19  	user, password string
    20  }
    21  
    22  func init() {
    23  	point.RegisterProtocol(NewClient)
    24  }
    25  
    26  func NewClient(config *protocol.Protocol_Http) point.WrapProxy {
    27  	return func(p netapi.Proxy) (netapi.Proxy, error) {
    28  		return &client{
    29  			Proxy:    p,
    30  			user:     config.Http.User,
    31  			password: config.Http.Password,
    32  		}, nil
    33  	}
    34  }
    35  
    36  func (c *client) Conn(ctx context.Context, s netapi.Address) (net.Conn, error) {
    37  	conn, err := c.Proxy.Conn(ctx, s)
    38  	if err != nil {
    39  		return nil, fmt.Errorf("dialer conn failed: %w", err)
    40  	}
    41  
    42  	req := &http.Request{
    43  		Method: http.MethodConnect,
    44  		URL:    &url.URL{},
    45  		Header: make(http.Header),
    46  		Host:   s.String(),
    47  	}
    48  
    49  	if c.user != "" || c.password != "" {
    50  		req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(c.user+":"+c.password)))
    51  	}
    52  
    53  	err = req.Write(conn)
    54  	if err != nil {
    55  		conn.Close()
    56  		return nil, fmt.Errorf("write request failed: %w", err)
    57  	}
    58  
    59  	bufioReader := bufio.NewReader(conn)
    60  	resp, err := http.ReadResponse(bufioReader, req)
    61  	if err != nil {
    62  		conn.Close()
    63  		return nil, fmt.Errorf("read response failed: %w", err)
    64  	}
    65  
    66  	if resp.StatusCode != http.StatusOK {
    67  		conn.Close()
    68  		return nil, fmt.Errorf("status code not ok: %d", resp.StatusCode)
    69  	}
    70  
    71  	conn, err = netapi.MergeBufioReaderConn(conn, bufioReader)
    72  	if err != nil {
    73  		conn.Close()
    74  		return nil, fmt.Errorf("merge bufio reader conn failed: %w", err)
    75  	}
    76  
    77  	return &clientConn{Conn: conn, resp: resp}, nil
    78  }
    79  
    80  type clientConn struct {
    81  	net.Conn
    82  	resp *http.Response
    83  }
    84  
    85  func (c *clientConn) Close() error {
    86  	c.resp.Body.Close()
    87  	return c.Conn.Close()
    88  }