httplib_compat.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # -*- coding: utf-8 -*-
  2. """
  3. hyper/httplib_compat
  4. ~~~~~~~~~~~~~~~~~~~~
  5. This file defines the publicly-accessible API for hyper. This API also
  6. constitutes the abstraction layer between HTTP/1.1 and HTTP/2.
  7. This API doesn't currently work, and is a lower priority than the HTTP/2
  8. stack at this time.
  9. """
  10. import socket
  11. try:
  12. import http.client as httplib
  13. except ImportError:
  14. import httplib
  15. from .compat import ssl
  16. from .http20.tls import wrap_socket
  17. # If there's no NPN support, we're going to drop all support for HTTP/2.
  18. try:
  19. support_20 = ssl.HAS_NPN
  20. except AttributeError:
  21. support_20 = False
  22. # The HTTPConnection object is currently always the underlying one.
  23. HTTPConnection = httplib.HTTPConnection
  24. HTTPSConnection = httplib.HTTPSConnection
  25. # If we have NPN support, define our custom one, otherwise just use the
  26. # default.
  27. if support_20:
  28. class HTTPSConnection(object):
  29. """
  30. An object representing a single HTTPS connection, whether HTTP/1.1 or
  31. HTTP/2.
  32. More specifically, this object represents an abstraction over the
  33. distinction. This object encapsulates a connection object for one of
  34. the specific types of connection, and delegates most of the work to
  35. that object.
  36. """
  37. def __init__(self, *args, **kwargs):
  38. # Whatever arguments and keyword arguments are passed to this
  39. # object need to be saved off for when we initialise one of our
  40. # subsidiary objects.
  41. self._original_args = args
  42. self._original_kwargs = kwargs
  43. # Set up some variables we're going to use later.
  44. self._sock = None
  45. self._conn = None
  46. # Prepare our backlog of method calls.
  47. self._call_queue = []
  48. def __getattr__(self, name):
  49. # Anything that can't be found on this instance is presumably a
  50. # property of underlying connection object.
  51. # We need to be a little bit careful here. There are a few methods
  52. # that can act on a HTTPSConnection before it actually connects to
  53. # the remote server. We don't want to change the semantics of the,
  54. # HTTPSConnection so we need to spot these and queue them up. When
  55. # we actually create the backing Connection, we'll apply them
  56. # immediately. These methods can't throw exceptions, so we should
  57. # be fine.
  58. delay_methods = ["set_tunnel", "set_debuglevel"]
  59. if self._conn is None and name in delay_methods:
  60. # Return a little closure that saves off the method call to
  61. # apply later.
  62. def capture(obj, *args, **kwargs):
  63. self._call_queue.append((name, args, kwargs))
  64. return capture
  65. elif self._conn is None:
  66. # We're being told to do something! We can now connect to the
  67. # remote server and build the connection object.
  68. self._delayed_connect()
  69. # Call through to the underlying object.
  70. return getattr(self._conn, name)
  71. def _delayed_connect(self):
  72. """
  73. Called when we need to work out what kind of HTTPS connection we're
  74. actually going to use.
  75. """
  76. # Because we're ghetto, we're going to quickly create a
  77. # HTTPConnection object to parse the args and kwargs for us, and
  78. # grab the values out.
  79. tempconn = httplib.HTTPConnection(*self._original_args,
  80. **self._original_kwargs)
  81. host = tempconn.host
  82. port = tempconn.port
  83. timeout = tempconn.timeout
  84. source_address = tempconn.source_address
  85. # Connect to the remote server.
  86. sock = socket.create_connection(
  87. (host, port),
  88. timeout,
  89. source_address
  90. )
  91. # Wrap it in TLS. This needs to be looked at in future when I pull
  92. # in the TLS verification logic from urllib3, but right now we
  93. # accept insecurity because no-one's using this anyway.
  94. sock = wrap_socket(sock, host)
  95. # At this early stage the library can't do HTTP/2, so who cares?
  96. tempconn.sock = sock
  97. self._sock = sock
  98. self._conn = tempconn
  99. return