This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
allow specifying https:// proxy #9119
Closed
Closed
Changes from 4 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
8529878
allow specifying https:// proxy
Bubu c9cb0a8
add back note about supported schemes
Bubu f889cdf
isort
Bubu 9326de3
Add test for connections to https proxy
richvdh ef657bf
fix comment wrapping
Bubu 76698d8
tls_options_factory isn't Optional
Bubu bca0c04
make proxy_parse tests a separate class
Bubu 728bbfb
Merge branch 'https_proxy' of github.com:Bubu/synapse into rework_pro…
Bubu 9275a29
add comment about supported proxy protocols to proxyagent
Bubu 59e6f5a
fix scheme, username:password order confusion in comment
Bubu 35378a0
Update synapse/http/proxyagent.py
richvdh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add support for https connections to a proxy server. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -22,6 +22,7 @@ | |||||||||
|
||||||||||
from twisted.internet import defer | ||||||||||
from twisted.internet.endpoints import HostnameEndpoint, wrapClientTLS | ||||||||||
from twisted.internet.interfaces import IReactorCore | ||||||||||
from twisted.python.failure import Failure | ||||||||||
from twisted.web.client import URI, BrowserLikePolicyForHTTPS, _AgentBase | ||||||||||
from twisted.web.error import SchemeNotSupported | ||||||||||
|
@@ -121,11 +122,11 @@ def __init__( | |||||||||
self.https_proxy_creds, https_proxy = parse_username_password(https_proxy) | ||||||||||
|
||||||||||
self.http_proxy_endpoint = _http_proxy_endpoint( | ||||||||||
http_proxy, self.proxy_reactor, **self._endpoint_kwargs | ||||||||||
http_proxy, self.proxy_reactor, contextFactory, **self._endpoint_kwargs | ||||||||||
) | ||||||||||
|
||||||||||
self.https_proxy_endpoint = _http_proxy_endpoint( | ||||||||||
https_proxy, self.proxy_reactor, **self._endpoint_kwargs | ||||||||||
https_proxy, self.proxy_reactor, contextFactory, **self._endpoint_kwargs | ||||||||||
) | ||||||||||
|
||||||||||
self.no_proxy = no_proxy | ||||||||||
|
@@ -243,28 +244,45 @@ def request(self, method, uri, headers=None, bodyProducer=None): | |||||||||
) | ||||||||||
|
||||||||||
|
||||||||||
def _http_proxy_endpoint(proxy: Optional[bytes], reactor, **kwargs): | ||||||||||
def _http_proxy_endpoint( | ||||||||||
proxy: Optional[bytes], | ||||||||||
reactor: IReactorCore, | ||||||||||
tls_options_factory: Optional[IPolicyForHTTPS], | ||||||||||
Bubu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
**kwargs, | ||||||||||
): | ||||||||||
"""Parses an http proxy setting and returns an endpoint for the proxy | ||||||||||
Bubu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
Args: | ||||||||||
proxy: the proxy setting in the form: [<username>:<password>@]<host>[:<port>] | ||||||||||
Note that compared to other apps, this function currently lacks support | ||||||||||
for specifying a protocol schema (i.e. protocol://...). | ||||||||||
proxy: the proxy setting in the form: [<username>:<password>@][scheme://]<host>[:<port>] | ||||||||||
Bubu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
This currently supports http:// and https:// proxies. a hostname without scheme is assumed to be http. | ||||||||||
Bubu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
reactor: reactor to be used to connect to the proxy | ||||||||||
|
||||||||||
tls_options_factory: the TLS options to use when connecting through a https proxy | ||||||||||
kwargs: other args to be passed to HostnameEndpoint | ||||||||||
|
||||||||||
Returns: | ||||||||||
interfaces.IStreamClientEndpoint|None: endpoint to use to connect to the proxy, | ||||||||||
or None | ||||||||||
|
||||||||||
Raises: ValueError if given a proxy with a scheme we don't support. | ||||||||||
Bubu marked this conversation as resolved.
Show resolved
Hide resolved
Bubu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
""" | ||||||||||
if proxy is None: | ||||||||||
return None | ||||||||||
|
||||||||||
# Parse the connection string | ||||||||||
host, port = parse_host_port(proxy, default_port=1080) | ||||||||||
return HostnameEndpoint(reactor, host, port, **kwargs) | ||||||||||
# Note: we can't use urlsplit/urlparse as that is completely broken for things without a scheme:// | ||||||||||
Bubu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
scheme, host, port = parse_proxy(proxy) | ||||||||||
|
||||||||||
if scheme not in (b"http", b"https"): | ||||||||||
raise ValueError("Proxy scheme '{}' not supported".format(scheme.decode())) | ||||||||||
|
||||||||||
proxy_endpoint = HostnameEndpoint(reactor, host, port, **kwargs) | ||||||||||
|
||||||||||
if scheme == b"https": | ||||||||||
tls_options = tls_options_factory.creatorForNetloc(host, port) | ||||||||||
proxy_endpoint = wrapClientTLS(tls_options, proxy_endpoint) | ||||||||||
|
||||||||||
return proxy_endpoint | ||||||||||
|
||||||||||
|
||||||||||
def parse_username_password(proxy: bytes) -> Tuple[Optional[ProxyCredentials], bytes]: | ||||||||||
|
@@ -288,25 +306,35 @@ def parse_username_password(proxy: bytes) -> Tuple[Optional[ProxyCredentials], b | |||||||||
return None, proxy | ||||||||||
|
||||||||||
|
||||||||||
def parse_host_port(hostport: bytes, default_port: int = None) -> Tuple[bytes, int]: | ||||||||||
def parse_proxy( | ||||||||||
proxy: bytes, default_scheme: bytes = b"http", default_port: int = 1080 | ||||||||||
) -> Tuple[bytes, bytes, int]: | ||||||||||
""" | ||||||||||
Parse the hostname and port from a proxy connection byte string. | ||||||||||
Parse the scheme, hostname and port from a proxy connection byte string. | ||||||||||
|
||||||||||
Args: | ||||||||||
hostport: The proxy connection string. Must be in the form 'host[:port]'. | ||||||||||
default_port: The default port to return if one is not found in `hostport`. | ||||||||||
proxy: The proxy connection string. Must be in the form '[scheme://]host[:port]'. | ||||||||||
default_scheme: The default scheme to return if one is not found in `proxy`. Defaults to http | ||||||||||
default_port: The default port to return if one is not found in `proxy`. Defaults to 1080 | ||||||||||
|
||||||||||
Returns: | ||||||||||
A tuple containing the hostname and port. Uses `default_port` if one was not found. | ||||||||||
A tuple containing the scheme, hostname and port. | ||||||||||
""" | ||||||||||
if b":" in hostport: | ||||||||||
host, port = hostport.rsplit(b":", 1) | ||||||||||
# First check if we have a scheme present | ||||||||||
if b"://" in proxy: | ||||||||||
scheme, host = proxy.split(b"://", 1) | ||||||||||
Comment on lines
+329
to
+330
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have thought about the problem with Python 3.9 and I would suggest to work with urlparse after all. For compatibility reasons we can check if there is a sheme and if not we add this. Somthing like this
Suggested change
|
||||||||||
else: | ||||||||||
scheme, host = default_scheme, proxy | ||||||||||
# Now check the leftover part for a port | ||||||||||
if b":" in host: | ||||||||||
new_host, port = host.rsplit(b":", 1) | ||||||||||
try: | ||||||||||
port = int(port) | ||||||||||
return host, port | ||||||||||
return scheme, new_host, port | ||||||||||
except ValueError: | ||||||||||
# the thing after the : wasn't a valid port; presumably this is an | ||||||||||
# IPv6 address. | ||||||||||
# TODO: this doesn't work when the last part of the IP is also just a number. | ||||||||||
# We probably need to require ipv6's being wrapped in square brackets: [2001:db8:0:0:1::1] | ||||||||||
pass | ||||||||||
|
||||||||||
return hostport, default_port | ||||||||||
return scheme, host, default_port |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will break for
https://username:password@host
, I think? Probably best to callparse_proxy
hereThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why only credentials were parsed for https proxy? Only security reason or anything else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest a solution like this.
This change can make
parse_username_password
compatible tohttps://username:password@host
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there is much reason at all. Support for credentials was added in #9657: perhaps
http_proxy
was just fogotten?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems sensible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition the issue for this #9000