dialer.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package http
  2. import (
  3. "context"
  4. gotls "crypto/tls"
  5. "net/http"
  6. "net/url"
  7. "sync"
  8. "golang.org/x/net/http2"
  9. "v2ray.com/core/common"
  10. "v2ray.com/core/common/buf"
  11. "v2ray.com/core/common/net"
  12. "v2ray.com/core/transport/internet"
  13. "v2ray.com/core/transport/internet/tls"
  14. "v2ray.com/core/transport/pipe"
  15. )
  16. var (
  17. globalDialerMap map[net.Destination]*http.Client
  18. globalDailerAccess sync.Mutex
  19. )
  20. func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.Config) (*http.Client, error) {
  21. globalDailerAccess.Lock()
  22. defer globalDailerAccess.Unlock()
  23. if globalDialerMap == nil {
  24. globalDialerMap = make(map[net.Destination]*http.Client)
  25. }
  26. if client, found := globalDialerMap[dest]; found {
  27. return client, nil
  28. }
  29. transport := &http2.Transport{
  30. DialTLS: func(network string, addr string, tlsConfig *gotls.Config) (net.Conn, error) {
  31. rawHost, rawPort, err := net.SplitHostPort(addr)
  32. if err != nil {
  33. return nil, err
  34. }
  35. if len(rawPort) == 0 {
  36. rawPort = "443"
  37. }
  38. port, err := net.PortFromString(rawPort)
  39. if err != nil {
  40. return nil, err
  41. }
  42. address := net.ParseAddress(rawHost)
  43. pconn, err := internet.DialSystem(context.Background(), net.TCPDestination(address, port), nil)
  44. if err != nil {
  45. return nil, err
  46. }
  47. return gotls.Client(pconn, tlsConfig), nil
  48. },
  49. TLSClientConfig: tlsSettings.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("h2")),
  50. }
  51. client := &http.Client{
  52. Transport: transport,
  53. }
  54. globalDialerMap[dest] = client
  55. return client, nil
  56. }
  57. // Dial dials a new TCP connection to the given destination.
  58. func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
  59. httpSettings := streamSettings.ProtocolSettings.(*Config)
  60. tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
  61. if tlsConfig == nil {
  62. return nil, newError("TLS must be enabled for http transport.").AtWarning()
  63. }
  64. client, err := getHTTPClient(ctx, dest, tlsConfig)
  65. if err != nil {
  66. return nil, err
  67. }
  68. opts := pipe.OptionsFromContext(ctx)
  69. preader, pwriter := pipe.New(opts...)
  70. breader := &buf.BufferedReader{Reader: preader}
  71. request := &http.Request{
  72. Method: "PUT",
  73. Host: httpSettings.getRandomHost(),
  74. Body: breader,
  75. URL: &url.URL{
  76. Scheme: "https",
  77. Host: dest.NetAddr(),
  78. Path: httpSettings.getNormalizedPath(),
  79. },
  80. Proto: "HTTP/2",
  81. ProtoMajor: 2,
  82. ProtoMinor: 0,
  83. Header: make(http.Header),
  84. }
  85. // Disable any compression method from server.
  86. request.Header.Set("Accept-Encoding", "identity")
  87. response, err := client.Do(request)
  88. if err != nil {
  89. return nil, newError("failed to dial to ", dest).Base(err).AtWarning()
  90. }
  91. if response.StatusCode != 200 {
  92. return nil, newError("unexpected status", response.StatusCode).AtWarning()
  93. }
  94. bwriter := buf.NewBufferedWriter(pwriter)
  95. common.Must(bwriter.SetBuffered(false))
  96. return net.NewConnection(
  97. net.ConnectionOutput(response.Body),
  98. net.ConnectionInput(bwriter),
  99. net.ConnectionOnClose(common.NewChainedClosable(breader, bwriter, response.Body)),
  100. ), nil
  101. }
  102. func init() {
  103. common.Must(internet.RegisterTransportDialer(protocolName, Dial))
  104. }