github.com/metacubex/mihomo@v1.18.5/transport/vmess/http.go (about)

     1  package vmess
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"net/textproto"
    11  
    12  	"github.com/metacubex/mihomo/common/utils"
    13  
    14  	"github.com/zhangyunhao116/fastrand"
    15  )
    16  
    17  type httpConn struct {
    18  	net.Conn
    19  	cfg        *HTTPConfig
    20  	reader     *bufio.Reader
    21  	whandshake bool
    22  }
    23  
    24  type HTTPConfig struct {
    25  	Method  string
    26  	Host    string
    27  	Path    []string
    28  	Headers map[string][]string
    29  }
    30  
    31  // Read implements net.Conn.Read()
    32  func (hc *httpConn) Read(b []byte) (int, error) {
    33  	if hc.reader != nil {
    34  		n, err := hc.reader.Read(b)
    35  		return n, err
    36  	}
    37  
    38  	reader := textproto.NewConn(hc.Conn)
    39  	// First line: GET /index.html HTTP/1.0
    40  	if _, err := reader.ReadLine(); err != nil {
    41  		return 0, err
    42  	}
    43  
    44  	if _, err := reader.ReadMIMEHeader(); err != nil {
    45  		return 0, err
    46  	}
    47  
    48  	hc.reader = reader.R
    49  	return reader.R.Read(b)
    50  }
    51  
    52  // Write implements io.Writer.
    53  func (hc *httpConn) Write(b []byte) (int, error) {
    54  	if hc.whandshake {
    55  		return hc.Conn.Write(b)
    56  	}
    57  
    58  	if len(hc.cfg.Path) == 0 {
    59  		return -1, errors.New("path is empty")
    60  	}
    61  
    62  	path := hc.cfg.Path[fastrand.Intn(len(hc.cfg.Path))]
    63  	host := hc.cfg.Host
    64  	if header := hc.cfg.Headers["Host"]; len(header) != 0 {
    65  		host = header[fastrand.Intn(len(header))]
    66  	}
    67  
    68  	u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path)
    69  	req, err := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b))
    70  	if err != nil {
    71  		return 0, err
    72  	}
    73  	for key, list := range hc.cfg.Headers {
    74  		req.Header.Set(key, list[fastrand.Intn(len(list))])
    75  	}
    76  	req.ContentLength = int64(len(b))
    77  	if err := req.Write(hc.Conn); err != nil {
    78  		return 0, err
    79  	}
    80  	hc.whandshake = true
    81  	return len(b), nil
    82  }
    83  
    84  func (hc *httpConn) Close() error {
    85  	return hc.Conn.Close()
    86  }
    87  
    88  func StreamHTTPConn(conn net.Conn, cfg *HTTPConfig) net.Conn {
    89  	return &httpConn{
    90  		Conn: conn,
    91  		cfg:  cfg,
    92  	}
    93  }