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 }