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