client.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package v2rayhttpupgrade
  2. import (
  3. std_bufio "bufio"
  4. "context"
  5. "net"
  6. "net/http"
  7. "net/url"
  8. "strings"
  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/common/buf"
  13. "github.com/sagernet/sing/common/bufio"
  14. E "github.com/sagernet/sing/common/exceptions"
  15. M "github.com/sagernet/sing/common/metadata"
  16. N "github.com/sagernet/sing/common/network"
  17. sHTTP "github.com/sagernet/sing/protocol/http"
  18. )
  19. var _ adapter.V2RayClientTransport = (*Client)(nil)
  20. type Client struct {
  21. dialer N.Dialer
  22. serverAddr M.Socksaddr
  23. requestURL url.URL
  24. headers http.Header
  25. host string
  26. }
  27. func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPUpgradeOptions, tlsConfig tls.Config) (*Client, error) {
  28. if tlsConfig != nil {
  29. if len(tlsConfig.NextProtos()) == 0 {
  30. tlsConfig.SetNextProtos([]string{"http/1.1"})
  31. }
  32. dialer = tls.NewDialer(dialer, tlsConfig)
  33. }
  34. var host string
  35. if options.Host != "" {
  36. host = options.Host
  37. } else if tlsConfig != nil && tlsConfig.ServerName() != "" {
  38. host = tlsConfig.ServerName()
  39. } else {
  40. host = serverAddr.String()
  41. }
  42. var requestURL url.URL
  43. if tlsConfig == nil {
  44. requestURL.Scheme = "http"
  45. } else {
  46. requestURL.Scheme = "https"
  47. }
  48. requestURL.Host = serverAddr.String()
  49. requestURL.Path = options.Path
  50. err := sHTTP.URLSetPath(&requestURL, options.Path)
  51. if err != nil {
  52. return nil, E.Cause(err, "parse path")
  53. }
  54. if !strings.HasPrefix(requestURL.Path, "/") {
  55. requestURL.Path = "/" + requestURL.Path
  56. }
  57. headers := make(http.Header)
  58. for key, value := range options.Headers {
  59. headers[key] = value
  60. }
  61. return &Client{
  62. dialer: dialer,
  63. serverAddr: serverAddr,
  64. requestURL: requestURL,
  65. headers: headers,
  66. host: host,
  67. }, nil
  68. }
  69. func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
  70. conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.serverAddr)
  71. if err != nil {
  72. return nil, err
  73. }
  74. request := &http.Request{
  75. Method: http.MethodGet,
  76. URL: &c.requestURL,
  77. Header: c.headers.Clone(),
  78. Host: c.host,
  79. }
  80. request.Header.Set("Connection", "Upgrade")
  81. request.Header.Set("Upgrade", "websocket")
  82. err = request.Write(conn)
  83. if err != nil {
  84. return nil, err
  85. }
  86. bufReader := std_bufio.NewReader(conn)
  87. response, err := http.ReadResponse(bufReader, request)
  88. if err != nil {
  89. return nil, err
  90. }
  91. if response.StatusCode != 101 ||
  92. !strings.EqualFold(response.Header.Get("Connection"), "upgrade") ||
  93. !strings.EqualFold(response.Header.Get("Upgrade"), "websocket") {
  94. return nil, E.New("v2ray-http-upgrade: unexpected status: ", response.Status)
  95. }
  96. if bufReader.Buffered() > 0 {
  97. buffer := buf.NewSize(bufReader.Buffered())
  98. _, err = buffer.ReadFullFrom(bufReader, buffer.Len())
  99. if err != nil {
  100. return nil, err
  101. }
  102. conn = bufio.NewCachedConn(conn, buffer)
  103. }
  104. return conn, nil
  105. }
  106. func (c *Client) Close() error {
  107. return nil
  108. }