client.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package v2raywebsocket
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "net"
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. "time"
  10. "github.com/sagernet/sing-box/adapter"
  11. "github.com/sagernet/sing-box/option"
  12. E "github.com/sagernet/sing/common/exceptions"
  13. M "github.com/sagernet/sing/common/metadata"
  14. N "github.com/sagernet/sing/common/network"
  15. "github.com/gorilla/websocket"
  16. )
  17. var _ adapter.V2RayClientTransport = (*Client)(nil)
  18. type Client struct {
  19. dialer *websocket.Dialer
  20. uri string
  21. headers http.Header
  22. maxEarlyData uint32
  23. earlyDataHeaderName string
  24. }
  25. func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayWebsocketOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
  26. wsDialer := &websocket.Dialer{
  27. NetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  28. return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
  29. },
  30. TLSClientConfig: tlsConfig,
  31. ReadBufferSize: 4 * 1024,
  32. WriteBufferSize: 4 * 1024,
  33. HandshakeTimeout: time.Second * 8,
  34. }
  35. var uri url.URL
  36. if tlsConfig == nil {
  37. uri.Scheme = "ws"
  38. } else {
  39. uri.Scheme = "wss"
  40. }
  41. uri.Host = serverAddr.String()
  42. uri.Path = options.Path
  43. if !strings.HasPrefix(uri.Path, "/") {
  44. uri.Path = "/" + uri.Path
  45. }
  46. headers := make(http.Header)
  47. for key, value := range options.Headers {
  48. headers.Set(key, value)
  49. }
  50. return &Client{
  51. wsDialer,
  52. uri.String(),
  53. headers,
  54. options.MaxEarlyData,
  55. options.EarlyDataHeaderName,
  56. }
  57. }
  58. func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
  59. if c.maxEarlyData <= 0 {
  60. conn, response, err := c.dialer.DialContext(ctx, c.uri, c.headers)
  61. if err == nil {
  62. return &WebsocketConn{Conn: conn}, nil
  63. }
  64. return nil, wrapDialError(response, err)
  65. } else {
  66. return &EarlyWebsocketConn{Client: c, create: make(chan struct{})}, nil
  67. }
  68. }
  69. func wrapDialError(response *http.Response, err error) error {
  70. if response == nil {
  71. return err
  72. }
  73. return E.Extend(err, "HTTP ", response.StatusCode, " ", response.Status)
  74. }