github.com/igoogolx/clash@v1.19.8/transport/vmess/http.go (about)

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