h2.go 2.0 KB

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