client.go 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package v2raygrpclite
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net"
  7. "net/http"
  8. "net/url"
  9. "github.com/sagernet/sing-box/adapter"
  10. "github.com/sagernet/sing-box/common/tls"
  11. "github.com/sagernet/sing-box/option"
  12. M "github.com/sagernet/sing/common/metadata"
  13. N "github.com/sagernet/sing/common/network"
  14. )
  15. var _ adapter.V2RayClientTransport = (*Client)(nil)
  16. var defaultClientHeader = http.Header{
  17. "Content-Type": []string{"application/grpc"},
  18. "User-Agent": []string{"grpc-go/1.48.0"},
  19. "TE": []string{"trailers"},
  20. }
  21. type Client struct {
  22. ctx context.Context
  23. dialer N.Dialer
  24. serverAddr M.Socksaddr
  25. transport *http.Transport
  26. options option.V2RayGRPCOptions
  27. url *url.URL
  28. }
  29. func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
  30. return &Client{
  31. ctx: ctx,
  32. dialer: dialer,
  33. serverAddr: serverAddr,
  34. options: options,
  35. transport: &http.Transport{
  36. DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  37. conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
  38. if err != nil {
  39. return nil, err
  40. }
  41. return tls.ClientHandshake(ctx, conn, tlsConfig)
  42. },
  43. ForceAttemptHTTP2: true,
  44. },
  45. url: &url.URL{
  46. Scheme: "https",
  47. Host: serverAddr.String(),
  48. Path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
  49. },
  50. }
  51. }
  52. func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
  53. pipeInReader, pipeInWriter := io.Pipe()
  54. request := &http.Request{
  55. Method: http.MethodPost,
  56. Body: pipeInReader,
  57. URL: c.url,
  58. Proto: "HTTP/2",
  59. ProtoMajor: 2,
  60. Header: defaultClientHeader,
  61. }
  62. request = request.WithContext(ctx)
  63. conn := newLateGunConn(pipeInWriter)
  64. go func() {
  65. response, err := c.transport.RoundTrip(request)
  66. if err == nil {
  67. conn.setup(response.Body, nil)
  68. } else {
  69. conn.setup(nil, err)
  70. }
  71. }()
  72. return conn, nil
  73. }