connection.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # -*- coding: utf-8 -*-
  2. """
  3. hyper/common/connection
  4. ~~~~~~~~~~~~~~~~~~~~~~~
  5. Hyper's HTTP/1.1 and HTTP/2 abstraction layer.
  6. """
  7. from .exceptions import TLSUpgrade, HTTPUpgrade
  8. from ..http11.connection import HTTP11Connection
  9. from ..http20.connection import HTTP20Connection
  10. from ..tls import H2_NPN_PROTOCOLS, H2C_PROTOCOL
  11. class HTTPConnection(object):
  12. """
  13. An object representing a single HTTP connection to a server.
  14. This object behaves similarly to the Python standard library's
  15. ``HTTPConnection`` object, with a few critical differences.
  16. Most of the standard library's arguments to the constructor are not
  17. supported by hyper. Most optional parameters apply to *either* HTTP/1.1 or
  18. HTTP/2.
  19. :param host: The host to connect to. This may be an IP address or a
  20. hostname, and optionally may include a port: for example,
  21. ``'http2bin.org'``, ``'http2bin.org:443'`` or ``'127.0.0.1'``.
  22. :param port: (optional) The port to connect to. If not provided and one
  23. also isn't provided in the ``host`` parameter, defaults to 80.
  24. :param secure: (optional) Whether the request should use TLS.
  25. Defaults to ``False`` for most requests, but to ``True`` for any
  26. request issued to port 443.
  27. :param window_manager: (optional) The class to use to manage flow control
  28. windows. This needs to be a subclass of the
  29. :class:`BaseFlowControlManager
  30. <hyper.http20.window.BaseFlowControlManager>`. If not provided,
  31. :class:`FlowControlManager <hyper.http20.window.FlowControlManager>`
  32. will be used.
  33. :param enable_push: (optional) Whether the server is allowed to push
  34. resources to the client (see
  35. :meth:`get_pushes() <hyper.HTTP20Connection.get_pushes>`).
  36. :param ssl_context: (optional) A class with custom certificate settings.
  37. If not provided then hyper's default ``SSLContext`` is used instead.
  38. :param proxy_host: (optional) The proxy to connect to. This can be an IP
  39. address or a host name and may include a port.
  40. :param proxy_port: (optional) The proxy port to connect to. If not provided
  41. and one also isn't provided in the ``proxy`` parameter, defaults to
  42. 8080.
  43. """
  44. def __init__(self,
  45. host,
  46. port=None,
  47. secure=None,
  48. window_manager=None,
  49. enable_push=False,
  50. ssl_context=None,
  51. proxy_host=None,
  52. proxy_port=None,
  53. **kwargs):
  54. self._host = host
  55. self._port = port
  56. self._h1_kwargs = {
  57. 'secure': secure, 'ssl_context': ssl_context,
  58. 'proxy_host': proxy_host, 'proxy_port': proxy_port
  59. }
  60. self._h2_kwargs = {
  61. 'window_manager': window_manager, 'enable_push': enable_push,
  62. 'secure': secure, 'ssl_context': ssl_context,
  63. 'proxy_host': proxy_host, 'proxy_port': proxy_port
  64. }
  65. # Add any unexpected kwargs to both dictionaries.
  66. self._h1_kwargs.update(kwargs)
  67. self._h2_kwargs.update(kwargs)
  68. self._conn = HTTP11Connection(
  69. self._host, self._port, **self._h1_kwargs
  70. )
  71. def request(self, method, url, body=None, headers=None):
  72. """
  73. This will send a request to the server using the HTTP request method
  74. ``method`` and the selector ``url``. If the ``body`` argument is
  75. present, it should be string or bytes object of data to send after the
  76. headers are finished. Strings are encoded as UTF-8. To use other
  77. encodings, pass a bytes object. The Content-Length header is set to the
  78. length of the body field.
  79. :param method: The request method, e.g. ``'GET'``.
  80. :param url: The URL to contact, e.g. ``'/path/segment'``.
  81. :param body: (optional) The request body to send. Must be a bytestring
  82. or a file-like object.
  83. :param headers: (optional) The headers to send on the request.
  84. :returns: A stream ID for the request, or ``None`` if the request is
  85. made over HTTP/1.1.
  86. """
  87. headers = headers or {}
  88. try:
  89. return self._conn.request(
  90. method=method, url=url, body=body, headers=headers
  91. )
  92. except TLSUpgrade as e:
  93. # We upgraded in the NPN/ALPN handshake. We can just go straight to
  94. # the world of HTTP/2. Replace the backing object and insert the
  95. # socket into it.
  96. assert e.negotiated in H2_NPN_PROTOCOLS
  97. self._conn = HTTP20Connection(
  98. self._host, self._port, **self._h2_kwargs
  99. )
  100. self._conn._sock = e.sock
  101. # Because we skipped the connecting logic, we need to send the
  102. # HTTP/2 preamble.
  103. self._conn._send_preamble()
  104. return self._conn.request(
  105. method=method, url=url, body=body, headers=headers
  106. )
  107. def get_response(self, *args, **kwargs):
  108. """
  109. Returns a response object.
  110. """
  111. try:
  112. return self._conn.get_response(*args, **kwargs)
  113. except HTTPUpgrade as e:
  114. # We upgraded via the HTTP Upgrade mechanism. We can just
  115. # go straight to the world of HTTP/2. Replace the backing object
  116. # and insert the socket into it.
  117. assert e.negotiated == H2C_PROTOCOL
  118. self._conn = HTTP20Connection(
  119. self._host, self._port, **self._h2_kwargs
  120. )
  121. self._conn._connect_upgrade(e.sock)
  122. # stream id 1 is used by the upgrade request and response
  123. # and is half-closed by the client
  124. return self._conn.get_response(1)
  125. # The following two methods are the implementation of the context manager
  126. # protocol.
  127. def __enter__(self): # pragma: no cover
  128. return self
  129. def __exit__(self, type, value, tb): # pragma: no cover
  130. self._conn.close()
  131. return False # Never swallow exceptions.
  132. # Can anyone say 'proxy object pattern'?
  133. def __getattr__(self, name):
  134. return getattr(self._conn, name)