123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- # -*- 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)
|