Handle urllib3 not being available

This commit is contained in:
coletdjnz 2024-04-06 15:40:29 +13:00
parent 3999a510f7
commit 01fe8e8fa6
No known key found for this signature in database
GPG Key ID: 91984263BB39894A
4 changed files with 57 additions and 25 deletions

View File

@ -156,7 +156,10 @@ def __init__(self, request, *args, **kwargs):
certfn = os.path.join(TEST_DIR, 'testcert.pem') certfn = os.path.join(TEST_DIR, 'testcert.pem')
sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sslctx.load_cert_chain(certfn, None) sslctx.load_cert_chain(certfn, None)
request = SSLTransport(request, ssl_context=sslctx, server_side=True) if SSLTransport:
request = SSLTransport(request, ssl_context=sslctx, server_side=True)
else:
request = sslctx.wrap_socket(request, server_side=True)
super().__init__(request, *args, **kwargs) super().__init__(request, *args, **kwargs)

View File

@ -4137,15 +4137,15 @@ def urlopen(self, req):
'Use --enable-file-urls to enable at your own risk.', cause=ue) from ue 'Use --enable-file-urls to enable at your own risk.', cause=ue) from ue
if ( if (
'unsupported proxy type: "https"' in ue.msg.lower() 'unsupported proxy type: "https"' in ue.msg.lower()
and 'requests' not in self._request_director.handlers and 'Requests' not in self._request_director.handlers
and 'curl_cffi' not in self._request_director.handlers and 'CurlCFFI' not in self._request_director.handlers
): ):
raise RequestError( raise RequestError(
'To use an HTTPS proxy for this request, one of the following dependencies needs to be installed: requests, curl_cffi') 'To use an HTTPS proxy for this request, one of the following dependencies needs to be installed: requests, curl_cffi')
elif ( elif (
re.match(r'unsupported url scheme: "wss?"', ue.msg.lower()) re.match(r'unsupported url scheme: "wss?"', ue.msg.lower())
and 'websockets' not in self._request_director.handlers and 'Websockets' not in self._request_director.handlers
): ):
raise RequestError( raise RequestError(
'This request requires WebSocket support. ' 'This request requires WebSocket support. '

View File

@ -19,20 +19,18 @@
ProxyError, ProxyError,
RequestError, RequestError,
SSLError, SSLError,
TransportError, TransportError, UnsupportedRequest,
) )
from .websocket import WebSocketRequestHandler, WebSocketResponse from .websocket import WebSocketRequestHandler, WebSocketResponse
from ..compat import functools from ..compat import functools
from ..dependencies import websockets from ..dependencies import websockets, urllib3
from ..socks import ProxyError as SocksProxyError from ..socks import ProxyError as SocksProxyError
from ..utils import int_or_none, extract_basic_auth from ..utils import int_or_none
import io import io
import urllib.parse import urllib.parse
import base64 import base64
from http.client import HTTPResponse, HTTPConnection, HTTPSConnection from http.client import HTTPResponse, HTTPConnection
from urllib3.util.ssltransport import SSLTransport
from ..utils.networking import HTTPHeaderDict from ..utils.networking import HTTPHeaderDict
@ -45,6 +43,11 @@
if websockets_version < (12, 0): if websockets_version < (12, 0):
raise ImportError('Only websockets>=12.0 is supported') raise ImportError('Only websockets>=12.0 is supported')
urllib3_supported = False
urllib3_version = tuple(int_or_none(x, default=0) for x in urllib3.__version__.split('.')) if urllib3 else None
if urllib3_version and urllib3_version >= (1, 26, 17):
urllib3_supported = True
import websockets.sync.client import websockets.sync.client
from websockets.uri import parse_uri from websockets.uri import parse_uri
@ -124,6 +127,17 @@ def __init__(self, *args, **kwargs):
if self.verbose: if self.verbose:
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
def _validate(self, request):
super()._validate(request)
proxy = select_proxy(request.url, self._get_proxies(request))
if (
proxy
and urllib.parse.urlparse(proxy).scheme.lower() == 'https'
and urllib.parse.urlparse(request.url).scheme.lower() == 'wss'
and not urllib3_supported
):
raise UnsupportedRequest('WSS over HTTPS proxies requires a supported version of urllib3')
def _check_extensions(self, extensions): def _check_extensions(self, extensions):
super()._check_extensions(extensions) super()._check_extensions(extensions)
extensions.pop('timeout', None) extensions.pop('timeout', None)
@ -178,6 +192,12 @@ def _send(self, request):
proxy = select_proxy(request.url, self._get_proxies(request)) proxy = select_proxy(request.url, self._get_proxies(request))
ssl_context = None
if parse_uri(request.url).secure:
if WebsocketsSSLContext is not None:
ssl_context = WebsocketsSSLContext(self._make_sslcontext())
else:
ssl_context = self._make_sslcontext()
try: try:
conn = websockets.sync.client.connect( conn = websockets.sync.client.connect(
sock=self._make_sock(proxy, request.url, timeout), sock=self._make_sock(proxy, request.url, timeout),
@ -185,10 +205,7 @@ def _send(self, request):
additional_headers=headers, additional_headers=headers,
open_timeout=timeout, open_timeout=timeout,
user_agent_header=None, user_agent_header=None,
ssl_context=( ssl_context=ssl_context,
WebsocketsSSLContext(self._make_sslcontext())
if parse_uri(request.url).secure else None
),
close_timeout=0, # not ideal, but prevents yt-dlp hanging close_timeout=0, # not ideal, but prevents yt-dlp hanging
) )
return WebsocketsResponseAdapter(conn, url=request.url) return WebsocketsResponseAdapter(conn, url=request.url)
@ -223,17 +240,21 @@ def begin(self):
self.will_close = False self.will_close = False
# todo: only define if urllib3 is available if urllib3_supported:
class WebsocketsSSLTransport(SSLTransport): from urllib3.util.ssltransport import SSLTransport
"""
Modified version of urllib3 SSLTransport to support additional operations used by websockets
"""
def setsockopt(self, *args, **kwargs):
self.socket.setsockopt(*args, **kwargs)
def shutdown(self, *args, **kwargs): class WebsocketsSSLTransport(SSLTransport):
self.unwrap() """
self.socket.shutdown(*args, **kwargs) Modified version of urllib3 SSLTransport to support additional operations used by websockets
"""
def setsockopt(self, *args, **kwargs):
self.socket.setsockopt(*args, **kwargs)
def shutdown(self, *args, **kwargs):
self.unwrap()
self.socket.shutdown(*args, **kwargs)
else:
WebsocketsSSLTransport = None
class WebsocketsSSLContext: class WebsocketsSSLContext:

View File

@ -1,8 +1,9 @@
from __future__ import annotations from __future__ import annotations
import abc import abc
import urllib.parse
from .common import RequestHandler, Response from .common import RequestHandler, Response, register_preference
class WebSocketResponse(Response): class WebSocketResponse(Response):
@ -21,3 +22,10 @@ def recv(self):
class WebSocketRequestHandler(RequestHandler, abc.ABC): class WebSocketRequestHandler(RequestHandler, abc.ABC):
pass pass
@register_preference(WebSocketRequestHandler)
def websocket_preference(_, request):
if urllib.parse.urlparse(request.url).scheme in ('ws', 'wss'):
return 200
return 0