[rh:curl_cffi] Support curl_cffi 0.16.x (#17439)

Closes #17341
Authored by: bashonly, coletdjnz

Co-authored-by: coletdjnz <coletdjnz@protonmail.com>
This commit is contained in:
bashonly
2026-08-15 23:50:51 +00:00
committed by GitHub
co-authored by coletdjnz
parent d2dcbfc574
commit 4dc054d51c
5 changed files with 42 additions and 9 deletions
-1
View File
@@ -97,7 +97,6 @@ BUNDLE_TARGETS = {
extras=['curl-cffi'], extras=['curl-cffi'],
# Only need curl-cffi+cffi in this requirements file; their deps are installed directly # Only need curl-cffi+cffi in this requirements file; their deps are installed directly
# XXX: Try to keep these in sync with curl-cffi's and cffi's transitive dependencies # XXX: Try to keep these in sync with curl-cffi's and cffi's transitive dependencies
prune_packages=['rich'],
omit_packages=['certifi', 'pycparser'], omit_packages=['certifi', 'pycparser'],
), ),
} }
+1 -1
View File
@@ -58,7 +58,7 @@ default = [
"yt-dlp-ejs==0.8.0", "yt-dlp-ejs==0.8.0",
] ]
curl-cffi = [ curl-cffi = [
"curl-cffi>=0.5.10,!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.16 ; implementation_name == 'cpython'", "curl-cffi>=0.5.10,!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.17 ; implementation_name == 'cpython'",
] ]
secretstorage = [ secretstorage = [
"secretstorage", "secretstorage",
+20
View File
@@ -83,6 +83,12 @@ class HTTPProxyHandler(BaseHTTPRequestHandler, HTTPProxyAuthMixin):
self.server.close_request(self.request) self.server.close_request(self.request)
def finish(self):
try:
super().finish()
finally:
self.server.close_request(self.request)
if urllib3: if urllib3:
import urllib3.util.ssltransport import urllib3.util.ssltransport
@@ -132,7 +138,11 @@ class HTTPSProxyHandler(HTTPProxyHandler):
request = SSLTransport(request, ssl_context=sslctx, server_side=True) request = SSLTransport(request, ssl_context=sslctx, server_side=True)
else: else:
request = sslctx.wrap_socket(request, server_side=True) request = sslctx.wrap_socket(request, server_side=True)
try:
super().__init__(request, *args, **kwargs) super().__init__(request, *args, **kwargs)
except Exception:
request.close()
raise
class HTTPConnectProxyHandler(BaseHTTPRequestHandler, HTTPProxyAuthMixin): class HTTPConnectProxyHandler(BaseHTTPRequestHandler, HTTPProxyAuthMixin):
@@ -163,6 +173,12 @@ class HTTPConnectProxyHandler(BaseHTTPRequestHandler, HTTPProxyAuthMixin):
self.request_handler(self.request, self.client_address, self.server, proxy_info=proxy_info) self.request_handler(self.request, self.client_address, self.server, proxy_info=proxy_info)
self.server.close_request(self.request) self.server.close_request(self.request)
def finish(self):
try:
super().finish()
finally:
self.server.close_request(self.request)
class HTTPSConnectProxyHandler(HTTPConnectProxyHandler): class HTTPSConnectProxyHandler(HTTPConnectProxyHandler):
def __init__(self, request, *args, **kwargs): def __init__(self, request, *args, **kwargs):
@@ -171,7 +187,11 @@ class HTTPSConnectProxyHandler(HTTPConnectProxyHandler):
sslctx.load_cert_chain(certfn, None) sslctx.load_cert_chain(certfn, None)
request = sslctx.wrap_socket(request, server_side=True) request = sslctx.wrap_socket(request, server_side=True)
self._original_request = request self._original_request = request
try:
super().__init__(request, *args, **kwargs) super().__init__(request, *args, **kwargs)
except Exception:
request.close()
raise
def do_CONNECT(self): def do_CONNECT(self):
super().do_CONNECT() super().do_CONNECT()
+12 -2
View File
@@ -388,13 +388,23 @@ class TestHTTPRequestHandler(TestRequestHandlerBase):
assert res.status == 200 assert res.status == 200
res.close() res.close()
def test_percent_encode(self, handler): def test_percent_encode_unicode(self, handler):
# RFC 3986 §6.2.2.1 defines that percent-encoding SHOULD be normalized to uppercase.
with handler() as rh: with handler() as rh:
# Unicode characters should be encoded with uppercase percent-encoding # Unicode characters should be encoded with uppercase percent-encoding
res = validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/中文.html')) res = validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/中文.html'))
assert res.status == 200 assert res.status == 200
res.close() res.close()
# don't normalize existing percent encodings
@pytest.mark.skip_handler('CurlCFFI', 'not supported by curl-cffi (non-standard)')
def test_percent_encode_keep_existing(self, handler):
# NOTE: RFC 3986 §6.2.2.1 defines that percent-encoding SHOULD be normalized to uppercase.
# For compatibility with legacy sites (e.g., redirects using lowercase encodings and only accept that),
# our default handlers (urllib/requests) preserve existing percent-encoding instead of normalizing it.
#
# CurlCFFI is excluded because it forces uppercase encodings and is hard to change. This is acceptable
# since CurlCFFI is used only for impersonation. https://github.com/curl/curl/pull/21592
with handler() as rh:
res = validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/%c7%9f')) res = validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/%c7%9f'))
assert res.status == 200 assert res.status == 200
res.close() res.close()
+7 -3
View File
@@ -33,9 +33,9 @@ if curl_cffi is None:
curl_cffi_version = tuple(map(int, re.split(r'[^\d]+', curl_cffi.__version__)[:3])) curl_cffi_version = tuple(map(int, re.split(r'[^\d]+', curl_cffi.__version__)[:3]))
if curl_cffi_version != (0, 5, 10) and not (0, 10) <= curl_cffi_version < (0, 16): if curl_cffi_version != (0, 5, 10) and not (0, 10) <= curl_cffi_version < (0, 17):
curl_cffi._yt_dlp__version = f'{curl_cffi.__version__} (unsupported)' curl_cffi._yt_dlp__version = f'{curl_cffi.__version__} (unsupported)'
raise ImportError('Only curl_cffi versions 0.5.10 and 0.10.x through 0.15.x are supported') raise ImportError('Only curl_cffi versions 0.5.10 and 0.10.x through 0.16.x are supported')
import curl_cffi.requests import curl_cffi.requests
from curl_cffi.const import CurlECode, CurlOpt from curl_cffi.const import CurlECode, CurlOpt
@@ -175,6 +175,9 @@ BROWSER_TARGETS: dict[tuple[int, ...], dict[str, ImpersonateTarget]] = {
'firefox144': ImpersonateTarget('firefox', '144', 'macos', '26'), 'firefox144': ImpersonateTarget('firefox', '144', 'macos', '26'),
'firefox147': ImpersonateTarget('firefox', '147', 'macos', '26'), 'firefox147': ImpersonateTarget('firefox', '147', 'macos', '26'),
}, },
(0, 16, 1): {
'chrome150': ImpersonateTarget('chrome', '150', 'macos', '26'),
},
} }
# Needed for curl_cffi < 0.11 # Needed for curl_cffi < 0.11
@@ -327,7 +330,8 @@ class CurlCFFIRH(ImpersonateRequestHandler, InstanceStoreMixin):
elif ( elif (
e.code == CurlECode.PROXY e.code == CurlECode.PROXY
or (e.code == CurlECode.RECV_ERROR and 'CONNECT' in str(e)) # curl_cffi >= 0.16.0: changed to CurlECode.COULDNT_CONNECT https://github.com/curl/curl/pull/21084
or (e.code in (CurlECode.RECV_ERROR, CurlECode.COULDNT_CONNECT) and 'CONNECT' in str(e))
): ):
raise ProxyError(cause=e) from e raise ProxyError(cause=e) from e
else: else: