github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/transport/internet/http/dialer.go (about) 1 // +build !confonly 2 3 package http 4 5 import ( 6 "context" 7 gotls "crypto/tls" 8 "net/http" 9 "net/url" 10 "sync" 11 12 "golang.org/x/net/http2" 13 "v2ray.com/core/common" 14 "v2ray.com/core/common/buf" 15 "v2ray.com/core/common/net" 16 "v2ray.com/core/transport/internet" 17 "v2ray.com/core/transport/internet/tls" 18 "v2ray.com/core/transport/pipe" 19 ) 20 21 var ( 22 globalDialerMap map[net.Destination]*http.Client 23 globalDialerAccess sync.Mutex 24 ) 25 26 func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.Config) (*http.Client, error) { 27 globalDialerAccess.Lock() 28 defer globalDialerAccess.Unlock() 29 30 if globalDialerMap == nil { 31 globalDialerMap = make(map[net.Destination]*http.Client) 32 } 33 34 if client, found := globalDialerMap[dest]; found { 35 return client, nil 36 } 37 38 transport := &http2.Transport{ 39 DialTLS: func(network string, addr string, tlsConfig *gotls.Config) (net.Conn, error) { 40 rawHost, rawPort, err := net.SplitHostPort(addr) 41 if err != nil { 42 return nil, err 43 } 44 if len(rawPort) == 0 { 45 rawPort = "443" 46 } 47 port, err := net.PortFromString(rawPort) 48 if err != nil { 49 return nil, err 50 } 51 address := net.ParseAddress(rawHost) 52 53 pconn, err := internet.DialSystem(context.Background(), net.TCPDestination(address, port), nil) 54 if err != nil { 55 return nil, err 56 } 57 58 cn := gotls.Client(pconn, tlsConfig) 59 if err := cn.Handshake(); err != nil { 60 return nil, err 61 } 62 if !tlsConfig.InsecureSkipVerify { 63 if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil { 64 return nil, err 65 } 66 } 67 state := cn.ConnectionState() 68 if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { 69 return nil, newError("http2: unexpected ALPN protocol " + p + "; want q" + http2.NextProtoTLS).AtError() 70 } 71 if !state.NegotiatedProtocolIsMutual { 72 return nil, newError("http2: could not negotiate protocol mutually").AtError() 73 } 74 return cn, nil 75 }, 76 TLSClientConfig: tlsSettings.GetTLSConfig(tls.WithDestination(dest)), 77 } 78 79 client := &http.Client{ 80 Transport: transport, 81 } 82 83 globalDialerMap[dest] = client 84 return client, nil 85 } 86 87 // Dial dials a new TCP connection to the given destination. 88 func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { 89 httpSettings := streamSettings.ProtocolSettings.(*Config) 90 tlsConfig := tls.ConfigFromStreamSettings(streamSettings) 91 if tlsConfig == nil { 92 return nil, newError("TLS must be enabled for http transport.").AtWarning() 93 } 94 client, err := getHTTPClient(ctx, dest, tlsConfig) 95 if err != nil { 96 return nil, err 97 } 98 99 opts := pipe.OptionsFromContext(ctx) 100 preader, pwriter := pipe.New(opts...) 101 breader := &buf.BufferedReader{Reader: preader} 102 request := &http.Request{ 103 Method: "PUT", 104 Host: httpSettings.getRandomHost(), 105 Body: breader, 106 URL: &url.URL{ 107 Scheme: "https", 108 Host: dest.NetAddr(), 109 Path: httpSettings.getNormalizedPath(), 110 }, 111 Proto: "HTTP/2", 112 ProtoMajor: 2, 113 ProtoMinor: 0, 114 Header: make(http.Header), 115 } 116 // Disable any compression method from server. 117 request.Header.Set("Accept-Encoding", "identity") 118 119 response, err := client.Do(request) 120 if err != nil { 121 return nil, newError("failed to dial to ", dest).Base(err).AtWarning() 122 } 123 if response.StatusCode != 200 { 124 return nil, newError("unexpected status", response.StatusCode).AtWarning() 125 } 126 127 bwriter := buf.NewBufferedWriter(pwriter) 128 common.Must(bwriter.SetBuffered(false)) 129 return net.NewConnection( 130 net.ConnectionOutput(response.Body), 131 net.ConnectionInput(bwriter), 132 net.ConnectionOnClose(common.ChainedClosable{breader, bwriter, response.Body}), 133 ), nil 134 } 135 136 func init() { 137 common.Must(internet.RegisterTransportDialer(protocolName, Dial)) 138 }