client.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. package v2raygrpclite
  2. import (
  3. "context"
  4. "io"
  5. "net"
  6. "net/http"
  7. "net/url"
  8. "time"
  9. "github.com/sagernet/sing-box/adapter"
  10. "github.com/sagernet/sing-box/common/tls"
  11. "github.com/sagernet/sing-box/option"
  12. "github.com/sagernet/sing-box/transport/v2rayhttp"
  13. F "github.com/sagernet/sing/common/format"
  14. M "github.com/sagernet/sing/common/metadata"
  15. N "github.com/sagernet/sing/common/network"
  16. "golang.org/x/net/http2"
  17. )
  18. var _ adapter.V2RayClientTransport = (*Client)(nil)
  19. var defaultClientHeader = http.Header{
  20. "Content-Type": []string{"application/grpc"},
  21. "User-Agent": []string{"grpc-go/1.48.0"},
  22. "TE": []string{"trailers"},
  23. }
  24. type Client struct {
  25. ctx context.Context
  26. dialer N.Dialer
  27. serverAddr M.Socksaddr
  28. transport *http2.Transport
  29. options option.V2RayGRPCOptions
  30. url *url.URL
  31. }
  32. func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
  33. client := &Client{
  34. ctx: ctx,
  35. dialer: dialer,
  36. serverAddr: serverAddr,
  37. options: options,
  38. transport: &http2.Transport{
  39. ReadIdleTimeout: time.Duration(options.IdleTimeout),
  40. PingTimeout: time.Duration(options.PingTimeout),
  41. DisableCompression: true,
  42. },
  43. url: &url.URL{
  44. Scheme: "https",
  45. Host: serverAddr.String(),
  46. Path: F.ToString("/", url.QueryEscape(options.ServiceName), "/Tun"),
  47. },
  48. }
  49. if tlsConfig == nil {
  50. client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
  51. return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
  52. }
  53. } else {
  54. if len(tlsConfig.NextProtos()) == 0 {
  55. tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
  56. }
  57. client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
  58. conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
  59. if err != nil {
  60. return nil, err
  61. }
  62. return tls.ClientHandshake(ctx, conn, tlsConfig)
  63. }
  64. }
  65. return client
  66. }
  67. func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
  68. pipeInReader, pipeInWriter := io.Pipe()
  69. request := &http.Request{
  70. Method: http.MethodPost,
  71. Body: pipeInReader,
  72. URL: c.url,
  73. Header: defaultClientHeader,
  74. }
  75. request = request.WithContext(ctx)
  76. conn := newLateGunConn(pipeInWriter)
  77. go func() {
  78. response, err := c.transport.RoundTrip(request)
  79. if err == nil {
  80. conn.setup(response.Body, nil)
  81. } else {
  82. conn.setup(nil, err)
  83. }
  84. }()
  85. return conn, nil
  86. }
  87. func (c *Client) Close() error {
  88. if c.transport != nil {
  89. v2rayhttp.CloseIdleConnections(c.transport)
  90. }
  91. return nil
  92. }