github.com/yaling888/clash@v1.53.0/transport/h2/h2.go (about) 1 package h2 2 3 import ( 4 "context" 5 "io" 6 "math/rand/v2" 7 "net" 8 "net/http" 9 "net/url" 10 "sync" 11 "time" 12 13 "golang.org/x/net/http2" 14 ) 15 16 type Config struct { 17 Hosts []string 18 Path string 19 Headers http.Header 20 } 21 22 var _ net.Conn = (*h2Conn)(nil) 23 24 type h2Conn struct { 25 net.Conn 26 *http2.ClientConn 27 pWriter *io.PipeWriter 28 resp *http.Response 29 cfg *Config 30 mux sync.Mutex 31 done chan struct{} 32 eErr error 33 } 34 35 func (hc *h2Conn) establishConn() error { 36 hc.mux.Lock() 37 defer hc.mux.Unlock() 38 39 select { 40 case <-hc.done: 41 return hc.eErr 42 default: 43 } 44 45 defer func() { 46 close(hc.done) 47 }() 48 49 pReader, pWriter := io.Pipe() 50 51 host := hc.cfg.Hosts[rand.IntN(len(hc.cfg.Hosts))] 52 path := hc.cfg.Path 53 headers := hc.cfg.Headers 54 headers.Del("Content-Type") 55 headers.Del("Content-Length") 56 headers.Set("Accept-Encoding", "identity") 57 // TODO: connect use VMess Host instead of H2 Host 58 req := &http.Request{ 59 Method: http.MethodPut, 60 Host: host, 61 URL: &url.URL{ 62 Scheme: "https", 63 Host: host, 64 Path: path, 65 }, 66 Proto: "HTTP/2", 67 ProtoMajor: 2, 68 ProtoMinor: 0, 69 Body: pReader, 70 Header: headers, 71 } 72 73 // it will be close at : `func (hc *h2Conn) Close() error` 74 resp, err := hc.ClientConn.RoundTrip(req) 75 if err != nil { 76 hc.eErr = err 77 return err 78 } 79 80 hc.pWriter = pWriter 81 hc.resp = resp 82 83 return nil 84 } 85 86 func (hc *h2Conn) Read(b []byte) (n int, err error) { 87 if hc.resp != nil { 88 return hc.resp.Body.Read(b) 89 } 90 91 <-hc.done 92 93 if hc.resp != nil { 94 return hc.resp.Body.Read(b) 95 } 96 97 err = hc.eErr 98 if err == nil { 99 err = io.EOF 100 } 101 102 return 103 } 104 105 func (hc *h2Conn) Write(b []byte) (n int, err error) { 106 if hc.pWriter != nil { 107 return hc.pWriter.Write(b) 108 } 109 110 if err = hc.establishConn(); err != nil { 111 return 112 } 113 114 if hc.pWriter != nil { 115 return hc.pWriter.Write(b) 116 } 117 118 err = hc.eErr 119 if err == nil { 120 err = net.ErrClosed 121 } 122 123 return 124 } 125 126 func (hc *h2Conn) Close() error { 127 if hc.pWriter != nil { 128 if err := hc.pWriter.Close(); err != nil { 129 return err 130 } 131 } 132 var ctx context.Context 133 if hc.resp != nil { 134 ctx = hc.resp.Request.Context() 135 } else { 136 ctx1, cancel := context.WithTimeout(context.Background(), time.Second) 137 defer cancel() 138 ctx = ctx1 139 } 140 if err := hc.ClientConn.Shutdown(ctx); err != nil { 141 return err 142 } 143 return hc.Conn.Close() 144 } 145 146 func StreamH2Conn(conn net.Conn, cfg *Config) (net.Conn, error) { 147 transport := &http2.Transport{} 148 149 cConn, err := transport.NewClientConn(conn) 150 if err != nil { 151 return nil, err 152 } 153 154 return &h2Conn{ 155 Conn: conn, 156 ClientConn: cConn, 157 cfg: cfg, 158 done: make(chan struct{}), 159 }, nil 160 }