github.com/kelleygo/clashcore@v1.0.2/transport/vmess/h2.go (about) 1 package vmess 2 3 import ( 4 "context" 5 "io" 6 "net" 7 "net/http" 8 "net/url" 9 10 "github.com/zhangyunhao116/fastrand" 11 "golang.org/x/net/http2" 12 ) 13 14 type h2Conn struct { 15 net.Conn 16 *http2.ClientConn 17 pwriter *io.PipeWriter 18 res *http.Response 19 cfg *H2Config 20 } 21 22 type H2Config struct { 23 Hosts []string 24 Path string 25 } 26 27 func (hc *h2Conn) establishConn() error { 28 preader, pwriter := io.Pipe() 29 30 host := hc.cfg.Hosts[fastrand.Intn(len(hc.cfg.Hosts))] 31 path := hc.cfg.Path 32 // TODO: connect use VMess Host instead of H2 Host 33 req := http.Request{ 34 Method: "PUT", 35 Host: host, 36 URL: &url.URL{ 37 Scheme: "https", 38 Host: host, 39 Path: path, 40 }, 41 Proto: "HTTP/2", 42 ProtoMajor: 2, 43 ProtoMinor: 0, 44 Body: preader, 45 Header: map[string][]string{ 46 "Accept-Encoding": {"identity"}, 47 }, 48 } 49 50 // it will be close at : `func (hc *h2Conn) Close() error` 51 res, err := hc.ClientConn.RoundTrip(&req) 52 if err != nil { 53 return err 54 } 55 56 hc.pwriter = pwriter 57 hc.res = res 58 59 return nil 60 } 61 62 // Read implements net.Conn.Read() 63 func (hc *h2Conn) Read(b []byte) (int, error) { 64 if hc.res != nil && !hc.res.Close { 65 n, err := hc.res.Body.Read(b) 66 return n, err 67 } 68 69 if err := hc.establishConn(); err != nil { 70 return 0, err 71 } 72 return hc.res.Body.Read(b) 73 } 74 75 // Write implements io.Writer. 76 func (hc *h2Conn) Write(b []byte) (int, error) { 77 if hc.pwriter != nil { 78 return hc.pwriter.Write(b) 79 } 80 81 if err := hc.establishConn(); err != nil { 82 return 0, err 83 } 84 return hc.pwriter.Write(b) 85 } 86 87 func (hc *h2Conn) Close() error { 88 if hc.pwriter != nil { 89 if err := hc.pwriter.Close(); err != nil { 90 return err 91 } 92 } 93 ctx := context.Background() 94 if hc.res != nil { 95 ctx = hc.res.Request.Context() 96 } 97 if err := hc.ClientConn.Shutdown(ctx); err != nil { 98 return err 99 } 100 return hc.Conn.Close() 101 } 102 103 func StreamH2Conn(conn net.Conn, cfg *H2Config) (net.Conn, error) { 104 transport := &http2.Transport{} 105 106 cconn, err := transport.NewClientConn(conn) 107 if err != nil { 108 return nil, err 109 } 110 111 return &h2Conn{ 112 Conn: conn, 113 ClientConn: cconn, 114 cfg: cfg, 115 }, nil 116 }