Skip to content

Commit

Permalink
Merge pull request #585 from azdagron/http-proxies-over-https
Browse files Browse the repository at this point in the history
support connecting to http proxies via https
  • Loading branch information
evan2645 authored Sep 11, 2018
2 parents 216acdc + 2a66ddb commit 96cccc2
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 18 deletions.
2 changes: 1 addition & 1 deletion pkg/common/grpcutil/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string) (*grpc.ClientConn, e
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

conn, err := proxyDial(ctx, addr)
conn, err := proxyDial(ctx, d.log, addr)
if err != nil {
d.log.Print(err)
return nil, err
Expand Down
48 changes: 31 additions & 17 deletions pkg/common/grpcutil/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,22 @@ package grpcutil

import (
"bufio"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"net/url"

"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"google.golang.org/grpc"
)

const grpcUA = "grpc-go/" + grpc.Version

func mapAddress(ctx context.Context, address string) (string, error) {
func getProxyURL(ctx context.Context, address string) (*url.URL, error) {
req := &http.Request{
URL: &url.URL{
Scheme: "https",
Expand All @@ -46,12 +48,17 @@ func mapAddress(ctx context.Context, address string) (string, error) {
}
url, err := http.ProxyFromEnvironment(req)
if err != nil {
return "", err
return nil, err
}
if url == nil {
return "", nil
return nil, nil
}
switch url.Scheme {
case "http", "https":
default:
return nil, fmt.Errorf("unsupported proxy scheme: %q", url.Scheme)
}
return url.Host, nil
return url, nil
}

// To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader.
Expand Down Expand Up @@ -106,26 +113,33 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, addr string) (_
// proxyDial dials to a proxy and does an HTTP CONNECT handshake if proxying is
// enabled. Otherwise, it just does a regular TCP dial. It is based on the
// newProxyDialer wrapper implementation from the gRPC codebase.
func proxyDial(ctx context.Context, addr string) (conn net.Conn, err error) {
var skipHandshake bool
newAddr, err := mapAddress(ctx, addr)
func proxyDial(ctx context.Context, log logrus.StdLogger, addr string) (conn net.Conn, err error) {
proxyURL, err := getProxyURL(ctx, addr)
if err != nil {
log.Printf("Failed to obtain proxy url for address %s: %v", addr, err)
return nil, err
}
if newAddr == "" {
skipHandshake = true
newAddr = addr

if proxyURL == nil {
// no proxy; dial the address directly
return new(net.Dialer).DialContext(ctx, "tcp", addr)
}

conn, err = new(net.Dialer).DialContext(ctx, "tcp", newAddr)
// Dial the proxy
log.Printf("Proxying via %s to reach %s", proxyURL.String(), addr)
conn, err = new(net.Dialer).DialContext(ctx, "tcp", proxyURL.Host)
if err != nil {
return nil, err
}
if !skipHandshake {
conn, err = doHTTPConnectHandshake(ctx, conn, addr)
if err != nil {
return nil, err
}

// if the proxy is over HTTPS, wrap the connection in a TLS connection
// before doing the HTTP CONNECT handshake.
if proxyURL.Scheme == "https" {
conn = tls.Client(conn, &tls.Config{
ServerName: proxyURL.Hostname(),
})
}
return conn, nil

// Do the HTTP-CONNECT handshake. If unsuccessful, conn is closed.
return doHTTPConnectHandshake(ctx, conn, addr)
}

0 comments on commit 96cccc2

Please sign in to comment.