Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements missing HTTPS proxy functionality #978

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

seans3
Copy link

@seans3 seans3 commented Feb 27, 2025

What type of PR is this? (check all applicable)

  • Feature

Description

  • Implements missing HTTPS Proxy functionality.
  • Adds both client/proxy/server tests and unit tests.

Related Tickets & Documents

Added/updated tests?

  • Yes

Run verifications and test

$ go test -race -cover
PASS
coverage: 84.2% of statements
ok  	github.com/gorilla/websocket	4.103s

@seans3
Copy link
Author

seans3 commented Feb 27, 2025

/cc @adrianosela

@seans3
Copy link
Author

seans3 commented Feb 27, 2025

/assign @liggitt
/assign @aojea

Copy link

@adrianosela adrianosela left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this @seans3 🚀

@aojea
Copy link

aojea commented Feb 27, 2025

IIUIC the same certificate is used for both the proxy and the backend, don't we need to handle the case when both are different? what happens if the proxy requires a different certificate than the backend?

EDIT

I see, the TLSConfig can be used for that, also @seans3 sent me this https://github.com/adrianosela/https-proxy/tree/main?tab=readme-ov-file#https-proxy

client.go Outdated
Comment on lines 429 to 431
if proxyURL == nil {
return nil, errors.New("generated proxy url is nil")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUIC this change the behavior, before if the returned proxyURL was nil meant "this connections should not be proxied" and now it will error , I think this should be just a noop

Suggested change
if proxyURL == nil {
return nil, errors.New("generated proxy url is nil")
}
// this request should not be proxied and use the original dialer
if proxyURL == nil {
return netDial, nil
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment on lines +432 to +434
if proxyURL.Scheme == "http" && d.NetDialTLSContext != nil {
return nil, errors.New("dial TLS function set for dialing non-HTTPS proxy")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not match the logic in

	var netDial netDialerFunc
	switch {
	case u.Scheme == "https" && d.NetDialTLSContext != nil:
		netDial = d.NetDialTLSContext
	case d.NetDialContext != nil:
		netDial = d.NetDialContext
	case d.NetDial != nil:
		netDial = func(ctx context.Context, net, addr string) (net.Conn, error) {
			return d.NetDial(net, addr)
		}
	default:
		netDial = (&net.Dialer{}).DialContext
	}

that seems to indicate that NetDialTLSContext is ONLY used if the scheme is https , but ignored otherwise

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems we can remove this check or just shortcut and

Suggested change
if proxyURL.Scheme == "http" && d.NetDialTLSContext != nil {
return nil, errors.New("dial TLS function set for dialing non-HTTPS proxy")
}
if proxyURL.Scheme == "http" {
return proxyFromURL(proxyURL, netDial)
}

Comment on lines +439 to +440
if d.NetDialTLSContext != nil {
netDial = d.NetDialTLSContext
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for self:

  • url is http and proxy-url is https, we replace the dialer ... users can specify a custom tls dialer just for the proxy.
  • url is https and proxy-url is https, the same tls dialer is used for both

Copy link
Author

@seans3 seans3 Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • url is https and proxy-url is https, the same tls dialer is used for both

I do not think this is correct. There is only one "dial". It is either to the "proxy" or to the "upstream" server. If there is a proxy, the dial is to the "proxy" (and the TLS handshake must occur in the dial). The "proxy" server is the place that dials the "upstream" in this case. The second TLS handshake (not dial) to the "upstream" occurs over the previously established connection, and it MUST use the TLSClientConfig. So the NetDialTLSContext is only used for the proxy connection if it is set and the Proxy function returns non-nil, HTTPS proxy URL.

@@ -29,7 +29,7 @@ func (fn netDialerFunc) DialContext(ctx context.Context, network, addr string) (
}

func proxyFromURL(proxyURL *url.URL, forwardDial netDialerFunc) (netDialerFunc, error) {
if proxyURL.Scheme == "http" {
if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it there other possibility? doesn't this mean, we always use the httpProxyDialer ?

Copy link
Author

@seans3 seans3 Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes--This will always use the httpProxyDialer. The difference is the forwardDial, which now will also perform the TLS handshake if necessary.

@seans3
Copy link
Author

seans3 commented Feb 27, 2025

IIUIC the same certificate is used for both the proxy and the backend, don't we need to handle the case when both are different? what happens if the proxy requires a different certificate than the backend?

The client is responsible for configuring the root CA certificates. The client TLS config contains a cert pool, which can contain multiple root CA certificates. So for TLS to the proxy and the upstream, the TLS config root CA cert pool must contain CA's which have signed both the proxy and the upstream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Is https not supported? [feature] Support for proxying websocket through https proxy
3 participants