123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- # -*- coding: utf-8 -*-
- """
- hyper/httplib_compat
- ~~~~~~~~~~~~~~~~~~~~
- This file defines the publicly-accessible API for hyper. This API also
- constitutes the abstraction layer between HTTP/1.1 and HTTP/2.
- This API doesn't currently work, and is a lower priority than the HTTP/2
- stack at this time.
- """
- import socket
- try:
- import http.client as httplib
- except ImportError:
- import httplib
- from .compat import ssl
- from .http20.tls import wrap_socket
- # If there's no NPN support, we're going to drop all support for HTTP/2.
- try:
- support_20 = ssl.HAS_NPN
- except AttributeError:
- support_20 = False
- # The HTTPConnection object is currently always the underlying one.
- HTTPConnection = httplib.HTTPConnection
- HTTPSConnection = httplib.HTTPSConnection
- # If we have NPN support, define our custom one, otherwise just use the
- # default.
- if support_20:
- class HTTPSConnection(object):
- """
- An object representing a single HTTPS connection, whether HTTP/1.1 or
- HTTP/2.
- More specifically, this object represents an abstraction over the
- distinction. This object encapsulates a connection object for one of
- the specific types of connection, and delegates most of the work to
- that object.
- """
- def __init__(self, *args, **kwargs):
- # Whatever arguments and keyword arguments are passed to this
- # object need to be saved off for when we initialise one of our
- # subsidiary objects.
- self._original_args = args
- self._original_kwargs = kwargs
- # Set up some variables we're going to use later.
- self._sock = None
- self._conn = None
- # Prepare our backlog of method calls.
- self._call_queue = []
- def __getattr__(self, name):
- # Anything that can't be found on this instance is presumably a
- # property of underlying connection object.
- # We need to be a little bit careful here. There are a few methods
- # that can act on a HTTPSConnection before it actually connects to
- # the remote server. We don't want to change the semantics of the,
- # HTTPSConnection so we need to spot these and queue them up. When
- # we actually create the backing Connection, we'll apply them
- # immediately. These methods can't throw exceptions, so we should
- # be fine.
- delay_methods = ["set_tunnel", "set_debuglevel"]
- if self._conn is None and name in delay_methods:
- # Return a little closure that saves off the method call to
- # apply later.
- def capture(obj, *args, **kwargs):
- self._call_queue.append((name, args, kwargs))
- return capture
- elif self._conn is None:
- # We're being told to do something! We can now connect to the
- # remote server and build the connection object.
- self._delayed_connect()
- # Call through to the underlying object.
- return getattr(self._conn, name)
- def _delayed_connect(self):
- """
- Called when we need to work out what kind of HTTPS connection we're
- actually going to use.
- """
- # Because we're ghetto, we're going to quickly create a
- # HTTPConnection object to parse the args and kwargs for us, and
- # grab the values out.
- tempconn = httplib.HTTPConnection(*self._original_args,
- **self._original_kwargs)
- host = tempconn.host
- port = tempconn.port
- timeout = tempconn.timeout
- source_address = tempconn.source_address
- # Connect to the remote server.
- sock = socket.create_connection(
- (host, port),
- timeout,
- source_address
- )
- # Wrap it in TLS. This needs to be looked at in future when I pull
- # in the TLS verification logic from urllib3, but right now we
- # accept insecurity because no-one's using this anyway.
- sock = wrap_socket(sock, host)
- # At this early stage the library can't do HTTP/2, so who cares?
- tempconn.sock = sock
- self._sock = sock
- self._conn = tempconn
- return
|