Aswin f8c4accd54 Reset
Reset dev
2025-03-18 00:17:27 +05:30

158 lines
6.1 KiB
Python

# -*- coding: utf-8 -*-
"""
hyper/common/connection
~~~~~~~~~~~~~~~~~~~~~~~
Hyper's HTTP/1.1 and HTTP/2 abstraction layer.
"""
from .exceptions import TLSUpgrade, HTTPUpgrade
from ..http11.connection import HTTP11Connection
from ..http20.connection import HTTP20Connection
from ..tls import H2_NPN_PROTOCOLS, H2C_PROTOCOL
class HTTPConnection(object):
"""
An object representing a single HTTP connection to a server.
This object behaves similarly to the Python standard library's
``HTTPConnection`` object, with a few critical differences.
Most of the standard library's arguments to the constructor are not
supported by hyper. Most optional parameters apply to *either* HTTP/1.1 or
HTTP/2.
:param host: The host to connect to. This may be an IP address or a
hostname, and optionally may include a port: for example,
``'http2bin.org'``, ``'http2bin.org:443'`` or ``'127.0.0.1'``.
:param port: (optional) The port to connect to. If not provided and one
also isn't provided in the ``host`` parameter, defaults to 80.
:param secure: (optional) Whether the request should use TLS.
Defaults to ``False`` for most requests, but to ``True`` for any
request issued to port 443.
:param window_manager: (optional) The class to use to manage flow control
windows. This needs to be a subclass of the
:class:`BaseFlowControlManager
<hyper.http20.window.BaseFlowControlManager>`. If not provided,
:class:`FlowControlManager <hyper.http20.window.FlowControlManager>`
will be used.
:param enable_push: (optional) Whether the server is allowed to push
resources to the client (see
:meth:`get_pushes() <hyper.HTTP20Connection.get_pushes>`).
:param ssl_context: (optional) A class with custom certificate settings.
If not provided then hyper's default ``SSLContext`` is used instead.
:param proxy_host: (optional) The proxy to connect to. This can be an IP
address or a host name and may include a port.
:param proxy_port: (optional) The proxy port to connect to. If not provided
and one also isn't provided in the ``proxy`` parameter, defaults to
8080.
"""
def __init__(self,
host,
port=None,
secure=None,
window_manager=None,
enable_push=False,
ssl_context=None,
proxy_host=None,
proxy_port=None,
**kwargs):
self._host = host
self._port = port
self._h1_kwargs = {
'secure': secure, 'ssl_context': ssl_context,
'proxy_host': proxy_host, 'proxy_port': proxy_port
}
self._h2_kwargs = {
'window_manager': window_manager, 'enable_push': enable_push,
'secure': secure, 'ssl_context': ssl_context,
'proxy_host': proxy_host, 'proxy_port': proxy_port
}
# Add any unexpected kwargs to both dictionaries.
self._h1_kwargs.update(kwargs)
self._h2_kwargs.update(kwargs)
self._conn = HTTP11Connection(
self._host, self._port, **self._h1_kwargs
)
def request(self, method, url, body=None, headers=None):
"""
This will send a request to the server using the HTTP request method
``method`` and the selector ``url``. If the ``body`` argument is
present, it should be string or bytes object of data to send after the
headers are finished. Strings are encoded as UTF-8. To use other
encodings, pass a bytes object. The Content-Length header is set to the
length of the body field.
:param method: The request method, e.g. ``'GET'``.
:param url: The URL to contact, e.g. ``'/path/segment'``.
:param body: (optional) The request body to send. Must be a bytestring
or a file-like object.
:param headers: (optional) The headers to send on the request.
:returns: A stream ID for the request, or ``None`` if the request is
made over HTTP/1.1.
"""
headers = headers or {}
try:
return self._conn.request(
method=method, url=url, body=body, headers=headers
)
except TLSUpgrade as e:
# We upgraded in the NPN/ALPN handshake. We can just go straight to
# the world of HTTP/2. Replace the backing object and insert the
# socket into it.
assert e.negotiated in H2_NPN_PROTOCOLS
self._conn = HTTP20Connection(
self._host, self._port, **self._h2_kwargs
)
self._conn._sock = e.sock
# Because we skipped the connecting logic, we need to send the
# HTTP/2 preamble.
self._conn._send_preamble()
return self._conn.request(
method=method, url=url, body=body, headers=headers
)
def get_response(self, *args, **kwargs):
"""
Returns a response object.
"""
try:
return self._conn.get_response(*args, **kwargs)
except HTTPUpgrade as e:
# We upgraded via the HTTP Upgrade mechanism. We can just
# go straight to the world of HTTP/2. Replace the backing object
# and insert the socket into it.
assert e.negotiated == H2C_PROTOCOL
self._conn = HTTP20Connection(
self._host, self._port, **self._h2_kwargs
)
self._conn._connect_upgrade(e.sock)
# stream id 1 is used by the upgrade request and response
# and is half-closed by the client
return self._conn.get_response(1)
# The following two methods are the implementation of the context manager
# protocol.
def __enter__(self): # pragma: no cover
return self
def __exit__(self, type, value, tb): # pragma: no cover
self._conn.close()
return False # Never swallow exceptions.
# Can anyone say 'proxy object pattern'?
def __getattr__(self, name):
return getattr(self._conn, name)