Skip to content

Commit

Permalink
Merge pull request #254 from shazow/factorout-connectioncls
Browse files Browse the repository at this point in the history
Factor out HTTP(S)Connection -> ConnectionCls, and cleanup.
  • Loading branch information
shazow committed Oct 16, 2013
2 parents f07dac4 + 8c65a6d commit 2948607
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 156 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ dev (master)
* Improved url parsing in ``urllib3.util.parse_url`` (properly parse '@' in
username, and blank ports like 'hostname:').

* New `urllib3.connection` module which contains all the HTTPConnection
objects.

* ...


1.7.1 (2013-09-25)
++++++++++++++++++
Expand Down
59 changes: 12 additions & 47 deletions test/with_dummyserver/test_https.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,14 @@ def test_verified(self):
self.assertTrue("doesn't match" in str(e))

def test_no_ssl(self):
import urllib3.connectionpool
OriginalHTTPSConnection = urllib3.connectionpool.HTTPSConnection
OriginalSSL = urllib3.connectionpool.ssl

urllib3.connectionpool.HTTPSConnection = None
urllib3.connectionpool.ssl = None
OriginalConnectionCls = self._pool.ConnectionCls
self._pool.ConnectionCls = None

self.assertRaises(SSLError, self._pool._new_conn)

self.assertRaises(SSLError, self._pool.request, 'GET', '/')

# Undo
urllib3.HTTPSConnection = OriginalHTTPSConnection
urllib3.connectionpool.ssl = OriginalSSL
self._pool.ConnectionCls = OriginalConnectionCls

def test_cert_reqs_as_constant(self):
https_pool = HTTPSConnectionPool(self.host, self.port,
Expand Down Expand Up @@ -223,57 +217,28 @@ def test_tunnel(self):


def test_enhanced_timeout(self):
import urllib3.connectionpool
OriginalHTTPSConnection = urllib3.connectionpool.HTTPSConnection
OriginalSSL = urllib3.connectionpool.ssl
def new_pool(timeout, cert_reqs='CERT_REQUIRED'):
https_pool = HTTPSConnectionPool(TARPIT_HOST, self.port,
timeout=timeout,
cert_reqs=cert_reqs)
return https_pool

urllib3.connectionpool.ssl = None

timeout = Timeout(connect=0.001)
https_pool = HTTPSConnectionPool(TARPIT_HOST, self.port,
timeout=timeout,
cert_reqs='CERT_REQUIRED')
https_pool = new_pool(Timeout(connect=0.001))
conn = https_pool._new_conn()
self.assertEqual(conn.__class__, HTTPSConnection)
self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/')
self.assertRaises(ConnectTimeoutError, https_pool._make_request, conn,
'GET', '/')

timeout = Timeout(connect=5)
https_pool = HTTPSConnectionPool(TARPIT_HOST, self.port,
timeout=timeout,
cert_reqs='CERT_REQUIRED')
https_pool = new_pool(Timeout(connect=5))
self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/',
timeout=Timeout(connect=0.001))

timeout = Timeout(total=None)
https_pool = HTTPSConnectionPool(TARPIT_HOST, self.port,
timeout=timeout,
cert_reqs='CERT_REQUIRED')
t = Timeout(total=None)
https_pool = new_pool(t)
conn = https_pool._new_conn()
self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/',
timeout=Timeout(total=None, connect=0.001))

https_pool = HTTPSConnectionPool(self.host, self.port,
timeout=timeout,
cert_reqs='CERT_NONE')
conn = https_pool._new_conn()
try:
conn.set_tunnel(self.host, self.port)
except AttributeError: # python 2.6
conn._set_tunnel(self.host, self.port)
conn._tunnel = mock.Mock()
try:
https_pool._make_request(conn, 'GET', '/')
except AttributeError:
# wrap_socket unavailable when you mock out ssl
pass
conn._tunnel.assert_called_once_with()

# Undo
urllib3.HTTPSConnection = OriginalHTTPSConnection
urllib3.connectionpool.ssl = OriginalSSL

def test_enhanced_ssl_connection(self):
conn = VerifiedHTTPSConnection(self.host, self.port)
https_pool = HTTPSConnectionPool(self.host, self.port,
Expand Down
107 changes: 107 additions & 0 deletions urllib3/connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# urllib3/connection.py
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

import socket
from socket import timeout as SocketTimeout

try: # Python 3
from http.client import HTTPConnection, HTTPException
except ImportError:
from httplib import HTTPConnection, HTTPException

class DummyConnection(object):
"Used to detect a failed ConnectionCls import."
pass

try: # Compiled with SSL?
ssl = None
HTTPSConnection = DummyConnection

class BaseSSLError(BaseException):
pass

try: # Python 3
from http.client import HTTPSConnection
except ImportError:
from httplib import HTTPSConnection

import ssl
BaseSSLError = ssl.SSLError

except (ImportError, AttributeError): # Platform-specific: No SSL.
pass

from .exceptions import (
ConnectTimeoutError,
)
from .packages.ssl_match_hostname import match_hostname
from .util import (
assert_fingerprint,
resolve_cert_reqs,
resolve_ssl_version,
ssl_wrap_socket,
)

class VerifiedHTTPSConnection(HTTPSConnection):
"""
Based on httplib.HTTPSConnection but wraps the socket with
SSL certification.
"""
cert_reqs = None
ca_certs = None
ssl_version = None

def set_cert(self, key_file=None, cert_file=None,
cert_reqs=None, ca_certs=None,
assert_hostname=None, assert_fingerprint=None):

self.key_file = key_file
self.cert_file = cert_file
self.cert_reqs = cert_reqs
self.ca_certs = ca_certs
self.assert_hostname = assert_hostname
self.assert_fingerprint = assert_fingerprint

def connect(self):
# Add certificate verification
try:
sock = socket.create_connection(
address=(self.host, self.port),
timeout=self.timeout,
)
except SocketTimeout:
raise ConnectTimeoutError(
self, "Connection to %s timed out. (connect timeout=%s)" %
(self.host, self.timeout))

resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs)
resolved_ssl_version = resolve_ssl_version(self.ssl_version)

if self._tunnel_host:
self.sock = sock
# Calls self._set_hostport(), so self.host is
# self._tunnel_host below.
self._tunnel()

# Wrap socket using verification with the root certs in
# trusted_root_certs
self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
cert_reqs=resolved_cert_reqs,
ca_certs=self.ca_certs,
server_hostname=self.host,
ssl_version=resolved_ssl_version)

if resolved_cert_reqs != ssl.CERT_NONE:
if self.assert_fingerprint:
assert_fingerprint(self.sock.getpeercert(binary_form=True),
self.assert_fingerprint)
elif self.assert_hostname is not False:
match_hostname(self.sock.getpeercert(),
self.assert_hostname or self.host)


if ssl:
HTTPSConnection = VerifiedHTTPSConnection
Loading

0 comments on commit 2948607

Please sign in to comment.