@@ -21,6 +21,19 @@ func (c *connWithBufferedReader) Read(p []byte) (int, error) {
2121 return c .r .Read (p )
2222}
2323
24+ // proxyDialAddr returns proxyURL.Host with a default port appended if one is
25+ // not already present (443 for https, 80 for http).
26+ func proxyDialAddr (proxyURL * url.URL ) string {
27+ addr := proxyURL .Host
28+ if _ , _ , err := net .SplitHostPort (addr ); err != nil {
29+ if proxyURL .Scheme == "https" {
30+ return net .JoinHostPort (addr , "443" )
31+ }
32+ return net .JoinHostPort (addr , "80" )
33+ }
34+ return addr
35+ }
36+
2437// withProxyTransport modifies the given transport to handle proxying of unix, socks5 and http connections.
2538//
2639// Note: baseTransport is considered to be a clone created with transport.Clone()
@@ -74,9 +87,38 @@ func withProxyTransport(baseTransport *http.Transport, proxyURL *url.URL, proxyP
7487 baseTransport .Proxy = http .ProxyURL (proxyURL )
7588 case "http" , "https" :
7689 dial := func (ctx context.Context , network , addr string ) (net.Conn , error ) {
77- // Dial the proxy
78- d := net.Dialer {}
79- conn , err := d .DialContext (ctx , "tcp" , proxyURL .Host )
90+ // Dial the proxy. For https:// proxies, we TLS-connect to the
91+ // proxy itself and force ALPN to HTTP/1.1 to prevent Go from
92+ // negotiating HTTP/2 for the CONNECT tunnel. Many proxy servers
93+ // don't support HTTP/2 CONNECT, and Go's default Transport.Proxy
94+ // would negotiate h2 via ALPN when TLS-connecting to an https://
95+ // proxy, causing "bogus greeting" errors. For http:// proxies,
96+ // CONNECT is always HTTP/1.1 over plain TCP so this isn't needed.
97+ // The target connection (e.g. to sourcegraph.com) still negotiates
98+ // HTTP/2 normally through the established tunnel.
99+ proxyAddr := proxyDialAddr (proxyURL )
100+
101+ var conn net.Conn
102+ var err error
103+ if proxyURL .Scheme == "https" {
104+ raw , dialErr := (& net.Dialer {}).DialContext (ctx , "tcp" , proxyAddr )
105+ if dialErr != nil {
106+ return nil , dialErr
107+ }
108+ cfg := baseTransport .TLSClientConfig .Clone ()
109+ cfg .NextProtos = []string {"http/1.1" }
110+ if cfg .ServerName == "" {
111+ cfg .ServerName = proxyURL .Hostname ()
112+ }
113+ tlsConn := tls .Client (raw , cfg )
114+ if err := tlsConn .HandshakeContext (ctx ); err != nil {
115+ raw .Close ()
116+ return nil , err
117+ }
118+ conn = tlsConn
119+ } else {
120+ conn , err = (& net.Dialer {}).DialContext (ctx , "tcp" , proxyAddr )
121+ }
80122 if err != nil {
81123 return nil , err
82124 }
0 commit comments