Make more of the internal handler helpers private. (#7510)

This commit is contained in:
Andrey Rakhmatullin 2026-05-13 01:03:14 +05:00 committed by GitHub
parent 2798c03bb0
commit 2d007bc450
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 36 additions and 27 deletions

View File

@ -9,10 +9,24 @@ Scrapy VERSION (unreleased)
Backward-incompatible changes Backward-incompatible changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``scrapy.core.downloader.handlers.http11.ScrapyProxyAgent`` has been made - The following classes and functions, intended for internal use by
private as it's an implementation detail of :class:`~scrapy.core.downloader.handlers.http11.HTTP11DownloadHandler`
:class:`~scrapy.core.downloader.handlers.http11.HTTP11DownloadHandler`. and :class:`~scrapy.core.downloader.handlers.http2.H2DownloadHandler`, have
(:issue:`7496`) been made private:
- ``scrapy.core.downloader.handlers.http11.ScrapyAgent``
- ``scrapy.core.downloader.handlers.http11.ScrapyProxyAgent``
- ``scrapy.core.downloader.handlers.http11.TunnelingAgent``
- ``scrapy.core.downloader.handlers.http11.TunnelingTCP4ClientEndpoint``
- ``scrapy.core.downloader.handlers.http11.tunnel_request_data()``
- ``scrapy.core.downloader.handlers.http2.ScrapyH2Agent``
(:issue:`7496`, #TBD)
.. _release-2.15.2: .. _release-2.15.2:

View File

@ -111,7 +111,7 @@ class HTTP11DownloadHandler(BaseHttpDownloadHandler):
"download_warnsize", "DOWNLOAD_WARNSIZE" "download_warnsize", "DOWNLOAD_WARNSIZE"
) )
agent = ScrapyAgent( agent = _ScrapyAgent(
contextFactory=self._contextFactory, contextFactory=self._contextFactory,
bindAddress=self._bind_address, bindAddress=self._bind_address,
pool=self._pool, pool=self._pool,
@ -161,7 +161,7 @@ class TunnelError(Exception):
"""An HTTP CONNECT tunnel could not be established by the proxy.""" """An HTTP CONNECT tunnel could not be established by the proxy."""
class TunnelingTCP4ClientEndpoint(TCP4ClientEndpoint): class _TunnelingTCP4ClientEndpoint(TCP4ClientEndpoint):
"""An endpoint that tunnels through proxies to allow HTTPS downloads. To """An endpoint that tunnels through proxies to allow HTTPS downloads. To
accomplish that, this endpoint sends an HTTP CONNECT to the proxy. accomplish that, this endpoint sends an HTTP CONNECT to the proxy.
The HTTP CONNECT is always sent when using this endpoint, I think this could The HTTP CONNECT is always sent when using this endpoint, I think this could
@ -197,7 +197,7 @@ class TunnelingTCP4ClientEndpoint(TCP4ClientEndpoint):
def requestTunnel(self, protocol: Protocol) -> Protocol: def requestTunnel(self, protocol: Protocol) -> Protocol:
"""Asks the proxy to open a tunnel.""" """Asks the proxy to open a tunnel."""
assert protocol.transport assert protocol.transport
tunnelReq = tunnel_request_data( tunnelReq = _tunnel_request_data(
self._tunneledHost, self._tunneledPort, self._proxyAuthHeader self._tunneledHost, self._tunneledPort, self._proxyAuthHeader
) )
protocol.transport.write(tunnelReq) protocol.transport.write(tunnelReq)
@ -221,7 +221,7 @@ class TunnelingTCP4ClientEndpoint(TCP4ClientEndpoint):
if b"\r\n\r\n" not in self._connectBuffer: if b"\r\n\r\n" not in self._connectBuffer:
return return
self._protocol.dataReceived = self._protocolDataReceived # type: ignore[method-assign] self._protocol.dataReceived = self._protocolDataReceived # type: ignore[method-assign]
respm = TunnelingTCP4ClientEndpoint._responseMatcher.match(self._connectBuffer) respm = _TunnelingTCP4ClientEndpoint._responseMatcher.match(self._connectBuffer)
if respm and int(respm.group("status")) == 200: if respm and int(respm.group("status")) == 200:
# set proper Server Name Indication extension # set proper Server Name Indication extension
sslOptions = self._contextFactory.creatorForNetloc( # type: ignore[call-arg,misc] sslOptions = self._contextFactory.creatorForNetloc( # type: ignore[call-arg,misc]
@ -258,18 +258,18 @@ class TunnelingTCP4ClientEndpoint(TCP4ClientEndpoint):
return self._tunnelReadyDeferred return self._tunnelReadyDeferred
def tunnel_request_data( def _tunnel_request_data(
host: str, port: int, proxy_auth_header: bytes | None = None host: str, port: int, proxy_auth_header: bytes | None = None
) -> bytes: ) -> bytes:
r""" r"""
Return binary content of a CONNECT request. Return binary content of a CONNECT request.
>>> from scrapy.utils.python import to_unicode as s >>> from scrapy.utils.python import to_unicode as s
>>> s(tunnel_request_data("example.com", 8080)) >>> s(_tunnel_request_data("example.com", 8080))
'CONNECT example.com:8080 HTTP/1.1\r\nHost: example.com:8080\r\n\r\n' 'CONNECT example.com:8080 HTTP/1.1\r\nHost: example.com:8080\r\n\r\n'
>>> s(tunnel_request_data("example.com", 8080, b"123")) >>> s(_tunnel_request_data("example.com", 8080, b"123"))
'CONNECT example.com:8080 HTTP/1.1\r\nHost: example.com:8080\r\nProxy-Authorization: 123\r\n\r\n' 'CONNECT example.com:8080 HTTP/1.1\r\nHost: example.com:8080\r\nProxy-Authorization: 123\r\n\r\n'
>>> s(tunnel_request_data(b"example.com", "8090")) >>> s(_tunnel_request_data(b"example.com", "8090"))
'CONNECT example.com:8090 HTTP/1.1\r\nHost: example.com:8090\r\n\r\n' 'CONNECT example.com:8090 HTTP/1.1\r\nHost: example.com:8090\r\n\r\n'
""" """
host_value = to_bytes(host, encoding="ascii") + b":" + to_bytes(str(port)) host_value = to_bytes(host, encoding="ascii") + b":" + to_bytes(str(port))
@ -281,7 +281,7 @@ def tunnel_request_data(
return tunnel_req return tunnel_req
class TunnelingAgent(Agent): class _TunnelingAgent(Agent):
"""An agent that uses a L{TunnelingTCP4ClientEndpoint} to make HTTPS """An agent that uses a L{TunnelingTCP4ClientEndpoint} to make HTTPS
downloads. It may look strange that we have chosen to subclass Agent and not downloads. It may look strange that we have chosen to subclass Agent and not
ProxyAgent but consider that after the tunnel is opened the proxy is ProxyAgent but consider that after the tunnel is opened the proxy is
@ -303,8 +303,8 @@ class TunnelingAgent(Agent):
self._proxyConf: tuple[str, int, bytes | None] = proxyConf self._proxyConf: tuple[str, int, bytes | None] = proxyConf
self._contextFactory: IPolicyForHTTPS = contextFactory self._contextFactory: IPolicyForHTTPS = contextFactory
def _getEndpoint(self, uri: URI) -> TunnelingTCP4ClientEndpoint: def _getEndpoint(self, uri: URI) -> _TunnelingTCP4ClientEndpoint:
return TunnelingTCP4ClientEndpoint( return _TunnelingTCP4ClientEndpoint(
reactor=self._reactor, reactor=self._reactor,
host=uri.host, host=uri.host,
port=uri.port, port=uri.port,
@ -381,10 +381,7 @@ class _ScrapyProxyAgent(Agent):
) )
class ScrapyAgent: class _ScrapyAgent:
_Agent = Agent
_TunnelingAgent = TunnelingAgent
def __init__( def __init__(
self, self,
*, *,
@ -430,7 +427,7 @@ class ScrapyAgent:
assert proxy_host is not None assert proxy_host is not None
proxyAuth = request.headers.get(b"Proxy-Authorization", None) proxyAuth = request.headers.get(b"Proxy-Authorization", None)
proxyConf = (proxy_host, proxy_port, proxyAuth) proxyConf = (proxy_host, proxy_port, proxyAuth)
return self._TunnelingAgent( return _TunnelingAgent(
reactor=reactor, reactor=reactor,
proxyConf=proxyConf, proxyConf=proxyConf,
contextFactory=self._contextFactory, contextFactory=self._contextFactory,
@ -447,7 +444,7 @@ class ScrapyAgent:
pool=self._pool, pool=self._pool,
) )
return self._Agent( # type: ignore[no-untyped-call] return Agent(
reactor=reactor, reactor=reactor,
contextFactory=self._contextFactory, contextFactory=self._contextFactory,
connectTimeout=timeout, connectTimeout=timeout,
@ -465,7 +462,7 @@ class ScrapyAgent:
url = urldefrag(request.url)[0] url = urldefrag(request.url)[0]
method = to_bytes(request.method) method = to_bytes(request.method)
headers = TxHeaders(request.headers) headers = TxHeaders(request.headers)
if isinstance(agent, self._TunnelingAgent): if isinstance(agent, _TunnelingAgent):
headers.removeHeader(b"Proxy-Authorization") headers.removeHeader(b"Proxy-Authorization")
bodyproducer = _RequestBodyProducer(request.body) if request.body else None bodyproducer = _RequestBodyProducer(request.body) if request.body else None
start_time = monotonic() start_time = monotonic()

View File

@ -49,7 +49,7 @@ class H2DownloadHandler(BaseDownloadHandler):
raise UnsupportedURLSchemeError( raise UnsupportedURLSchemeError(
f"{type(self).__name__} doesn't support plain HTTP." f"{type(self).__name__} doesn't support plain HTTP."
) )
agent = ScrapyH2Agent( agent = _ScrapyH2Agent(
context_factory=self._context_factory, context_factory=self._context_factory,
pool=self._pool, pool=self._pool,
bind_address=self._bind_address, bind_address=self._bind_address,
@ -65,9 +65,7 @@ class H2DownloadHandler(BaseDownloadHandler):
self._pool.close_connections() self._pool.close_connections()
class ScrapyH2Agent: class _ScrapyH2Agent:
_Agent = H2Agent
def __init__( def __init__(
self, self,
context_factory: IPolicyForHTTPS, context_factory: IPolicyForHTTPS,
@ -89,7 +87,7 @@ class ScrapyH2Agent:
raise NotImplementedError(f"{type(self).__name__} doesn't support proxies.") raise NotImplementedError(f"{type(self).__name__} doesn't support proxies.")
bind_address = request.meta.get("bindaddress") or self._bind_address bind_address = request.meta.get("bindaddress") or self._bind_address
bind_address = normalize_bind_address(bind_address) bind_address = normalize_bind_address(bind_address)
return self._Agent( return H2Agent(
reactor=reactor, reactor=reactor,
context_factory=self._context_factory, context_factory=self._context_factory,
connect_timeout=timeout, connect_timeout=timeout,