Description
parse_proxy() takes username and password from urllib.parse.urlparse(...) as they are, without percent-decoding them. A password that contains a reserved character has to be percent-encoded to be representable in the URL at all (socks5://alice:p%40ss@host:1080 for the password p@ss), but websockets then sends the literal string p%40ss to the proxy: connect_socks_proxy() hands proxy.username / proxy.password to python-socks unchanged, and connect_http_proxy() base64-encodes them unchanged for Proxy-Authorization.
python-socks itself (python_socks._helpers.parse_proxy_url) applies unquote() to both, so the same URL works with python-socks directly and with other clients that build on it, but not through websockets' proxy= parameter. RFC 3986 section 3.2.1 defines userinfo as percent-encoded.
Reproduction
>>> from websockets.proxy import parse_proxy
>>> p = parse_proxy("socks5://alice:p%40ss@127.0.0.1:1080")
>>> p.password
'p%40ss'
Against a SOCKS5 proxy with user/password auth the connection is rejected (ProxyError: failed to connect to SOCKS proxy), against an HTTP CONNECT proxy with basic auth it is answered with 407.
Environment
websockets 16.0, Python 3.13.5, python-socks 3.1.1, Linux x86_64.
Expected
parse_proxy() percent-decodes username and password (e.g. urllib.parse.unquote), like python-socks and requests do for proxy URLs, so that credentials with @, :, / or % can be used.
Description
parse_proxy()takesusernameandpasswordfromurllib.parse.urlparse(...)as they are, without percent-decoding them. A password that contains a reserved character has to be percent-encoded to be representable in the URL at all (socks5://alice:p%40ss@host:1080for the passwordp@ss), but websockets then sends the literal stringp%40ssto the proxy:connect_socks_proxy()handsproxy.username/proxy.passwordto python-socks unchanged, andconnect_http_proxy()base64-encodes them unchanged forProxy-Authorization.python-socks itself (
python_socks._helpers.parse_proxy_url) appliesunquote()to both, so the same URL works with python-socks directly and with other clients that build on it, but not through websockets'proxy=parameter. RFC 3986 section 3.2.1 defines userinfo as percent-encoded.Reproduction
Against a SOCKS5 proxy with user/password auth the connection is rejected (
ProxyError: failed to connect to SOCKS proxy), against an HTTP CONNECT proxy with basic auth it is answered with 407.Environment
websockets 16.0, Python 3.13.5, python-socks 3.1.1, Linux x86_64.
Expected
parse_proxy()percent-decodesusernameandpassword(e.g.urllib.parse.unquote), likepython-socksandrequestsdo for proxy URLs, so that credentials with@,:,/or%can be used.