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  }