Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
dev
-------------------

- Make ``socks_scheme`` arguments case-insensitive. (#82)

0.17.0 (2025-05-31)
-------------------

Expand Down
12 changes: 10 additions & 2 deletions pypac/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def parse_pac_value(value, socks_scheme=None):

:param str value: Any value returned by ``FindProxyForURL()``.
:param str socks_scheme: Scheme to assume for SOCKS proxies. ``socks5`` by default.
Case-insensitive.
:returns: Parsed output, with invalid elements ignored. Warnings are logged for invalid elements.
:rtype: list[str]
"""
Expand All @@ -145,6 +146,9 @@ def parse_pac_value(value, socks_scheme=None):
return config


_PROXY_SCHEMES = {"HTTP", "HTTPS", "SOCKS4", "SOCKS5"}


def proxy_url(value, socks_scheme=None):
"""
Parse a single proxy config value from FindProxyForURL() into a more usable element.
Expand All @@ -154,6 +158,7 @@ def proxy_url(value, socks_scheme=None):

:param str value: Value to parse, e.g.: ``DIRECT``, ``PROXY example.local:8080``, or ``SOCKS example.local:8080``.
:param str socks_scheme: Scheme to assume for SOCKS proxies. ``socks5`` by default.
Case-insensitive.
:returns: Parsed value, e.g.: ``DIRECT``, ``http://example.local:8080``, or ``socks5://example.local:8080``.
:rtype: str
:raises ValueError: If input value is invalid.
Expand All @@ -167,9 +172,12 @@ def proxy_url(value, socks_scheme=None):
if keyword == "PROXY":
keyword = "HTTP"
elif keyword == "SOCKS":
keyword = socks_scheme or "SOCKS5"
socks_scheme = socks_scheme.upper() if socks_scheme else "SOCKS5"
if socks_scheme not in _PROXY_SCHEMES:
raise ValueError("socks_scheme '{}' is not one of {}".format(socks_scheme, ", ".join(_PROXY_SCHEMES)))
keyword = socks_scheme

if keyword in ("HTTP", "HTTPS", "SOCKS4", "SOCKS5"):
if keyword in _PROXY_SCHEMES:
return "{0}://{1}".format(keyword.lower(), proxy)

raise ValueError("Unrecognized proxy config value '{}'".format(value))
1 change: 1 addition & 0 deletions pypac/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, pac, proxy_auth=None, socks_scheme="socks5"):
:param requests.auth.HTTPProxyAuth proxy_auth: Username and password proxy authentication.
If provided, then all proxy URLs returned will include these credentials.
:param str socks_scheme: Scheme to assume for SOCKS proxies. `socks5` by default.
Case-insensitive.
"""
self.pac = pac
self._proxy_auth = proxy_auth
Expand Down
5 changes: 5 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ class TestFindProxyForURLOutputParsing(object):
def test_parse_single_value(self, pac_value, expected_result):
assert parse_pac_value(pac_value) == [expected_result]
assert proxy_url(pac_value) == expected_result
if pac_value.startswith("SOCKS "):
# socks_scheme must be case-insensitive
assert proxy_url(pac_value, socks_scheme="Socks4") == "socks4://foo:8080"
with pytest.raises(ValueError):
assert proxy_url(pac_value, socks_scheme="bad")

def test_multiple(self):
assert parse_pac_value("PROXY foo:8080; DIRECT") == ["http://foo:8080", "DIRECT"]
Expand Down
1 change: 1 addition & 0 deletions tests/test_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def _get_resolver(js_func_return_value, proxy_auth=None):
[
"DIRECT",
"PROXY foo:8080",
"SOCKS foo:8080",
],
)
def test_single_pac_value(pac_value):
Expand Down
Loading