Skip to content

Commit

Permalink
Fix CONNECT with downstream HTTP proxy
Browse files Browse the repository at this point in the history
This patch fixes and extends connectHTTP() implementation.
We make separate CONNECT request to the downstream proxy to avoid copying everything we read from the wire.
It may contain "per-proxy-hop" headers and other information we do not want to send as well as body.
We send Proxy-Authorization header if UserInfo is provided.
We do TLS handshake with HTTPS proxies.
We fix the Content-Length -1 in CONNECT response issues.

Fixes google#300
  • Loading branch information
mmatczuk committed Nov 21, 2022
1 parent ee14268 commit ddc8332
Showing 1 changed file with 35 additions and 5 deletions.
40 changes: 35 additions & 5 deletions proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bufio"
"bytes"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -667,16 +668,45 @@ func (p *Proxy) connectHTTP(req *http.Request, proxyURL *url.URL) (*http.Respons
if err != nil {
return nil, nil, err
}
if proxyURL.Scheme == "https" {
tlsConfig := p.clientTLCConfig()
tlsConfig.ServerName = proxyURL.Hostname()
tlsConfig.NextProtos = []string{"http/1.1"}
conn = tls.Client(conn, tlsConfig)
}

pbw := bufio.NewWriter(conn)
pbr := bufio.NewReader(conn)

req.Write(pbw)
pbw.Flush()
connReq := &http.Request{
Method: "CONNECT",
URL: req.URL,
Host: req.Host,
Header: make(http.Header),
}
if proxyURL.User != nil {
connReq.Header.Add("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(proxyURL.User.String())))
}
connReq.Write(pbw)

res, err := http.ReadResponse(pbr, req)
if err != nil {
deadline := time.Now().Add(p.timeout)
conn.SetDeadline(deadline)
if err := pbw.Flush(); err != nil {
return nil, nil, err
}

return res, conn, nil
res, err := http.ReadResponse(pbr, req)
if err != nil || res.StatusCode/100 != 2 {
return res, nil, err
}

return proxyutil.NewResponse(200, nil, req), conn, nil
}

func (p *Proxy) clientTLCConfig() *tls.Config {
if tr, ok := p.roundTripper.(*http.Transport); ok && tr.TLSClientConfig != nil {
return tr.TLSClientConfig.Clone()
}

return &tls.Config{}
}

0 comments on commit ddc8332

Please sign in to comment.