Beispiel #1
0
class TestHTTPS_TLSv1(HTTPSDummyServerTestCase):
    certs = DEFAULT_CERTS.copy()
    certs["ssl_version"] = ssl.PROTOCOL_TLSv1

    def setUp(self):
        self._pool = HTTPSConnectionPool(self.host, self.port)

    def test_set_ssl_version_to_sslv3(self):
        self._pool.ssl_version = ssl.PROTOCOL_SSLv3
        self.assertRaises(SSLError, self._pool.request, "GET", "/")

    def test_ssl_version_as_string(self):
        self._pool.ssl_version = "PROTOCOL_SSLv3"
        self.assertRaises(SSLError, self._pool.request, "GET", "/")

    def test_ssl_version_as_short_string(self):
        self._pool.ssl_version = "SSLv3"
        self.assertRaises(SSLError, self._pool.request, "GET", "/")

    def test_discards_connection_on_sslerror(self):
        self._pool.cert_reqs = "CERT_REQUIRED"
        self.assertRaises(SSLError, self._pool.request, "GET", "/")
        self._pool.ca_certs = DEFAULT_CA
        self._pool.request("GET", "/")

    def test_set_cert_default_cert_required(self):
        conn = VerifiedHTTPSConnection(self.host, self.port)
        conn.set_cert(ca_certs="/etc/ssl/certs/custom.pem")
        self.assertEqual(conn.cert_reqs, "CERT_REQUIRED")
Beispiel #2
0
 def test_invalid_common_name(self):
     https_pool = HTTPSConnectionPool("127.0.0.1", self.port, cert_reqs="CERT_REQUIRED", ca_certs=DEFAULT_CA)
     try:
         https_pool.request("GET", "/")
         self.fail("Didn't raise SSL invalid common name")
     except SSLError as e:
         self.assertTrue("doesn't match" in str(e))
Beispiel #3
0
    def test_assert_fingerprint_sha256(self):
        https_pool = HTTPSConnectionPool("localhost", self.port, cert_reqs="CERT_REQUIRED", ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = (
            "9A:29:9D:4F:47:85:1C:51:23:F5:9A:A3:" "0F:5A:EF:96:F9:2E:3C:22:2E:FC:E8:BC:" "0E:73:90:37:ED:3B:AA:AB"
        )
        https_pool.request("GET", "/")
Beispiel #4
0
 def request():
     try:
         pool = HTTPSConnectionPool(self.host, self.port, assert_fingerprint=fingerprint)
         response = pool.urlopen("GET", "/", preload_content=False, timeout=Timeout(connect=1, read=0.001))
         response.read()
     finally:
         pool.close()
Beispiel #5
0
 def test_can_validate_ip_san(self):
     """Ensure that urllib3 can validate SANs with IP addresses in them."""
     https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                      cert_reqs='CERT_REQUIRED',
                                      ca_certs=DEFAULT_CA)
     r = https_pool.request('GET', '/')
     self.assertEqual(r.status, 200)
Beispiel #6
0
    def test_verified(self):
        https_pool = HTTPSConnectionPool(self.host, self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

        with mock.patch('warnings.warn') as warn:
            r = https_pool.request('GET', '/')
            self.assertEqual(r.status, 200)

            # Modern versions of Python, or systems using PyOpenSSL, don't
            # emit warnings.
            if sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL \
                    or util.IS_SECURETRANSPORT:
                self.assertFalse(warn.called, warn.call_args_list)
            else:
                self.assertTrue(warn.called)
                if util.HAS_SNI:
                    call = warn.call_args_list[0]
                else:
                    call = warn.call_args_list[1]
                error = call[0][1]
                self.assertEqual(error, InsecurePlatformWarning)
Beispiel #7
0
class TestHTTPS_TLSv1(HTTPSDummyServerTestCase):
    certs = DEFAULT_CERTS.copy()
    certs['ssl_version'] = ssl.PROTOCOL_TLSv1

    def setUp(self):
        self._pool = HTTPSConnectionPool(self.host, self.port)

    def test_set_ssl_version_to_sslv3(self):
        self._pool.ssl_version = ssl.PROTOCOL_SSLv3
        self.assertRaises(SSLError, self._pool.request, 'GET', '/')

    def test_ssl_version_as_string(self):
        self._pool.ssl_version = 'PROTOCOL_SSLv3'
        self.assertRaises(SSLError, self._pool.request, 'GET', '/')

    def test_ssl_version_as_short_string(self):
        self._pool.ssl_version = 'SSLv3'
        self.assertRaises(SSLError, self._pool.request, 'GET', '/')

    def test_discards_connection_on_sslerror(self):
        self._pool.cert_reqs = 'CERT_REQUIRED'
        self.assertRaises(SSLError, self._pool.request, 'GET', '/')
        self._pool.ca_certs = DEFAULT_CA
        self._pool.request('GET', '/')

    def test_set_cert_default_cert_required(self):
        conn = VerifiedHTTPSConnection(self.host, self.port)
        conn.set_cert(ca_certs=DEFAULT_CA)
        self.assertEqual(conn.cert_reqs, 'CERT_REQUIRED')
Beispiel #8
0
    def test_assert_hostname_false(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_hostname = False
        https_pool.request('GET', '/')
Beispiel #9
0
    def test_ssl_verified_with_platform_ca_certs(self):
        """
        We should rely on the platform CA file to validate authenticity of SSL
        certificates. Since this file is used by many components of the OS,
        such as curl, apt-get, etc., we decided to not touch it, in order to
        not compromise the security of the OS running the test suite (typically
        urllib3 developer's OS).

        This test assumes that httpbin.org uses a certificate signed by a well
        known Certificate Authority.
        """
        try:
            import urllib3.contrib.pyopenssl
        except ImportError:
            raise SkipTest('Test requires PyOpenSSL')
        if (urllib3.connection.ssl_wrap_socket is
                urllib3.contrib.pyopenssl.orig_connection_ssl_wrap_socket):
            # Not patched
            raise SkipTest('Test should only be run after PyOpenSSL '
                           'monkey patching')

        https_pool = HTTPSConnectionPool('httpbin.org', 443,
                                         cert_reqs=ssl.CERT_REQUIRED)

        https_pool.request('HEAD', '/')
Beispiel #10
0
    def test_invalid_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host, self.port, cert_reqs="CERT_REQUIRED")

        # Empty string won't throw on py2
        https_pool.ca_certs = "/no_valid_path_to_ca_certs"

        self.assertRaises(SSLError, https_pool.request, "GET", "/")
Beispiel #11
0
    def test_verified_with_context(self):
        ctx = util.ssl_.create_urllib3_context(cert_reqs=ssl.CERT_REQUIRED)
        ctx.load_verify_locations(cafile=DEFAULT_CA)
        https_pool = HTTPSConnectionPool(self.host, self.port,
                                         ssl_context=ctx)
        self.addCleanup(https_pool.close)

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

        with mock.patch('warnings.warn') as warn:
            r = https_pool.request('GET', '/')
            self.assertEqual(r.status, 200)

            # Modern versions of Python, or systems using PyOpenSSL, don't
            # emit warnings.
            if sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL:
                self.assertFalse(warn.called, warn.call_args_list)
            else:
                self.assertTrue(warn.called)
                if util.HAS_SNI:
                    call = warn.call_args_list[0]
                else:
                    call = warn.call_args_list[1]
                error = call[0][1]
                self.assertEqual(error, InsecurePlatformWarning)
Beispiel #12
0
    def test_assert_specific_hostname(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_hostname = 'localhost'
        https_pool.request('GET', '/')
Beispiel #13
0
 def test_strip_square_brackets_before_validating(self):
     """Test that the fix for #760 works."""
     if not HAS_IPV6:
         raise SkipTest("Only runs on IPv6 systems")
     https_pool = HTTPSConnectionPool("[::1]", self.port, cert_reqs="CERT_REQUIRED", ca_certs=IPV6_ADDR_CA)
     r = https_pool.request("GET", "/")
     self.assertEqual(r.status, 200)
Beispiel #14
0
    def test_cert_reqs_as_constant(self):
        https_pool = HTTPSConnectionPool(self.host, self.port,
                                         cert_reqs=ssl.CERT_REQUIRED)

        https_pool.ca_certs = DEFAULT_CA_BAD
        # if we pass in an invalid value it defaults to CERT_NONE
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')
Beispiel #15
0
    def test_assert_fingerprint_md5(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CA:84:E1:AD0E5a:ef:2f:C3:09' \
                                        ':E7:30:F8:CD:C8:5B'
        https_pool.request('GET', '/')
Beispiel #16
0
 def test_source_address(self):
     for addr in VALID_SOURCE_ADDRESSES:
         https_pool = HTTPSConnectionPool(
             self.host, self.port, cert_reqs='CERT_REQUIRED',
             source_address=addr)
         https_pool.ca_certs = DEFAULT_CA
         r = https_pool.request('GET', '/source_address')
         assert r.data == b(addr[0])
Beispiel #17
0
 def test_source_address_error(self):
     for addr in INVALID_SOURCE_ADDRESSES:
         https_pool = HTTPSConnectionPool(
             self.host, self.port, cert_reqs='CERT_REQUIRED',
             source_address=addr)
         https_pool.ca_certs = DEFAULT_CA
         self.assertRaises(
             MaxRetryError, https_pool.request, 'GET', '/source_address')
Beispiel #18
0
    def test_assert_specific_hostname(self):
        https_pool = HTTPSConnectionPool('localhost', self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_hostname = 'localhost'
        https_pool.request('GET', '/')
Beispiel #19
0
 def test_warning_for_certs_without_a_san(self):
     """Ensure that a warning is raised when the cert from the server has
     no Subject Alternative Name."""
     with mock.patch("warnings.warn") as warn:
         https_pool = HTTPSConnectionPool(self.host, self.port, cert_reqs="CERT_REQUIRED", ca_certs=NO_SAN_CA)
         r = https_pool.request("GET", "/")
         self.assertEqual(r.status, 200)
         self.assertTrue(warn.called)
Beispiel #20
0
    def test_verify_none_and_good_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_NONE',
                                         ca_certs=DEFAULT_CA_BAD)

        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'
        https_pool.request('GET', '/')
Beispiel #21
0
    def test_assert_fingerprint_sha1(self):
        https_pool = HTTPSConnectionPool('localhost', self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'
        https_pool.request('GET', '/')
Beispiel #22
0
 def test_no_ssl(self):
     pool = HTTPSConnectionPool(self.host, self.port)
     pool.ConnectionCls = None
     self.addCleanup(pool.close)
     self.assertRaises(SSLError, pool._new_conn)
     with self.assertRaises(MaxRetryError) as cm:
         pool.request('GET', '/', retries=0)
     self.assertIsInstance(cm.exception.reason, SSLError)
Beispiel #23
0
    def test_good_fingerprint_and_hostname_mismatch(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'
        https_pool.request('GET', '/')
Beispiel #24
0
 def test_strip_square_brackets_before_validating(self):
     """Test that the fix for #760 works."""
     https_pool = HTTPSConnectionPool('[::1]', self.port,
                                      cert_reqs='CERT_REQUIRED',
                                      ca_certs=IPV6_ADDR_CA)
     self.addCleanup(https_pool.close)
     r = https_pool.request('GET', '/')
     self.assertEqual(r.status, 200)
Beispiel #25
0
    def test_verify_none_and_bad_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_NONE',
                                         ca_certs=DEFAULT_CA_BAD)

        https_pool.assert_fingerprint = 'AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:' \
                                        'AA:AA:AA:AA:AA:AA:AA:AA:AA'
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')
Beispiel #26
0
    def test_verify_none_and_good_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_NONE',
                                         ca_certs=DEFAULT_CA_BAD)

        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool.request('GET', '/')
Beispiel #27
0
    def test_assert_fingerprint_sha1(self):
        https_pool = HTTPSConnectionPool('127.0.0.1', self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool.request('GET', '/')
Beispiel #28
0
 def test_enhanced_ssl_connection(self):
     conn = VerifiedHTTPSConnection(self.host, self.port)
     https_pool = HTTPSConnectionPool(
         self.host, self.port, timeout=Timeout(total=None, connect=5), cert_reqs="CERT_REQUIRED"
     )
     https_pool.ca_certs = DEFAULT_CA
     https_pool.assert_fingerprint = "CC:45:6A:90:82:F7FF:C0:8218:8e:" "7A:F2:8A:D7:1E:07:33:67:DE"
     https_pool._make_request(conn, "GET", "/")
Beispiel #29
0
    def test_enhanced_ssl_connection(self):
        fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:7A:F2:8A:D7:1E:07:33:67:DE'

        conn = VerifiedHTTPSConnection(self.host, self.port)
        https_pool = HTTPSConnectionPool(self.host, self.port,
                cert_reqs='CERT_REQUIRED', ca_certs=DEFAULT_CA,
                assert_fingerprint=fingerprint)

        https_pool._make_request(conn, 'GET', '/')
Beispiel #30
0
    def test_enhanced_ssl_connection(self):
        fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:BF:93:CF:F9:71:CC:07:7D:0A'

        conn = VerifiedHTTPSConnection(self.host, self.port)
        https_pool = HTTPSConnectionPool(self.host, self.port,
                cert_reqs='CERT_REQUIRED', ca_certs=DEFAULT_CA,
                assert_fingerprint=fingerprint)

        https_pool._make_request(conn, 'GET', '/')
	def __init__(self):
		self.handle = HTTPSConnectionPool(RapleafApi.host, timeout = RapleafApi.timeout)
Beispiel #32
0
class TestHTTPS(HTTPSDummyServerTestCase):
    def setUp(self):
        self._pool = HTTPSConnectionPool(self.host, self.port)

    def test_simple(self):
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_set_ssl_version_to_tlsv1(self):
        self._pool.ssl_version = ssl.PROTOCOL_TLSv1
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_verified(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

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

        https_pool.ca_certs = DEFAULT_CA_BAD

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL error with wrong CA")
        except SSLError as e:
            self.assertTrue(
                'certificate verify failed' in str(e),
                "Expected 'certificate verify failed',"
                "instead got: %r" % e)

        https_pool.ca_certs = DEFAULT_CA
        https_pool.request('GET', '/')  # Should succeed without exceptions.

        https_fail_pool = HTTPSConnectionPool('127.0.0.1',
                                              self.port,
                                              cert_reqs='CERT_REQUIRED')
        https_fail_pool.ca_certs = DEFAULT_CA

        try:
            https_fail_pool.request('GET', '/')
            self.fail("Didn't raise SSL invalid common name")
        except SSLError as e:
            self.assertTrue("doesn't match" in str(e))

    def test_no_ssl(self):
        OriginalConnectionCls = self._pool.ConnectionCls
        try:
            self._pool.ConnectionCls = None

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

        finally:
            self._pool.ConnectionCls = OriginalConnectionCls

    def test_unverified_ssl(self):
        """ Test that bare HTTPSConnection can connect, make requests """
        try:
            OriginalConnectionCls = self._pool.ConnectionCls
            self._pool.ConnectionCls = UnverifiedHTTPSConnection
            self._pool.request('GET', '/')

        finally:
            self._pool.ConnectionCls = OriginalConnectionCls

    def test_cert_reqs_as_constant(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs=ssl.CERT_REQUIRED)

        https_pool.ca_certs = DEFAULT_CA_BAD
        # if we pass in an invalid value it defaults to CERT_NONE
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')

    def test_cert_reqs_as_short_string(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='REQUIRED')

        https_pool.ca_certs = DEFAULT_CA_BAD
        # if we pass in an invalid value it defaults to CERT_NONE
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')

    def test_ssl_unverified_with_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_NONE')

        https_pool.ca_certs = DEFAULT_CA_BAD
        https_pool.request('GET', '/')

    @requires_network
    def test_ssl_verified_with_platform_ca_certs(self):
        """
        This test check that whe rely on platform CA file to validate
        authenticity of SSL certificate. Since this file is used by many
        components of the OS, such as curl, apt-get, etc., we decided to not
        touch it, in order to not compromise the security of the OS
        running the test suite (typically urllib3 developer's OS).

        This test assume that httpbin.org use a certificate signed
        by a well known Certificate Authority.
        """
        try:
            import urllib3.contrib.pyopenssl
        except ImportError:
            raise SkipTest('This test needs pyopenssl support')
        if (urllib3.connection.ssl_wrap_socket is
                urllib3.contrib.pyopenssl.orig_connection_ssl_wrap_socket):
            # Not patched
            raise SkipTest('This test needs pyopenssl support')

        https_pool = HTTPSConnectionPool('httpbin.org',
                                         443,
                                         cert_reqs=ssl.CERT_REQUIRED)

        https_pool.request('HEAD', '/')

    def test_verified_without_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

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

    def test_invalid_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        # Empty string won't throw on py2
        https_pool.ca_certs = '/no_valid_path_to_ca_certs'

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

    def test_assert_hostname_false(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_hostname = False
        https_pool.request('GET', '/')

    def test_assert_specific_hostname(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_hostname = 'localhost'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_md5(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CA:84:E1:AD0E5a:ef:2f:C3:09' \
                                        ':E7:30:F8:CD:C8:5B'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_sha1(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool.request('GET', '/')

    def test_assert_invalid_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:' \
                                        'AA:AA:AA:AA:AA:AA:AA:AA:AA'

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

        # invalid length
        https_pool.assert_fingerprint = 'AA'

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

        # uneven length
        https_pool.assert_fingerprint = 'AA:A'

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

    @requires_network
    def test_https_timeout(self):
        timeout = Timeout(connect=0.001)
        https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_REQUIRED')

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

        timeout = Timeout(read=0.001)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_REQUIRED')
        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        url = '/sleep?seconds=0.005'
        self.assertRaises(ReadTimeoutError, https_pool.request, 'GET', url)

        timeout = Timeout(total=None)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_NONE')
        https_pool.request('GET', '/')

    def test_tunnel(self):
        """ test the _tunnel behavior """
        timeout = Timeout(total=None)
        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()
        https_pool._make_request(conn, 'GET', '/')
        conn._tunnel.assert_called_once_with()

    def test_tunnel_old_python(self):
        """HTTPSConnection can still make connections if _tunnel_host isn't set

        The _tunnel_host attribute was added in 2.6.3 - because our test runners
        generally use the latest Python 2.6, we simulate the old version by
        deleting the attribute from the HTTPSConnection.
        """
        conn = self._pool._new_conn()
        del conn._tunnel_host
        self._pool._make_request(conn, 'GET', '/')

    @requires_network
    def test_enhanced_timeout(self):
        def new_pool(timeout, cert_reqs='CERT_REQUIRED'):
            https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                             self.port,
                                             timeout=timeout,
                                             cert_reqs=cert_reqs)
            return https_pool

        https_pool = new_pool(Timeout(connect=0.001))
        conn = https_pool._new_conn()
        self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/')
        self.assertRaises(ConnectTimeoutError, https_pool._make_request, conn,
                          'GET', '/')

        https_pool = new_pool(Timeout(connect=5))
        self.assertRaises(ConnectTimeoutError,
                          https_pool.request,
                          'GET',
                          '/',
                          timeout=Timeout(connect=0.001))

        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))

    def test_enhanced_ssl_connection(self):
        conn = VerifiedHTTPSConnection(self.host, self.port)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=Timeout(total=None,
                                                         connect=5),
                                         cert_reqs='CERT_REQUIRED')
        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool._make_request(conn, 'GET', '/')
Beispiel #33
0
 def test_simple(self):
     with HTTPSConnectionPool(self.host, self.port,
                              ca_certs=DEFAULT_CA) as https_pool:
         r = https_pool.request("GET", "/")
         assert r.status == 200, r.data
Beispiel #34
0
 def setup_method(self, method):
     self._pool = HTTPSConnectionPool(self.host,
                                      self.port,
                                      ca_certs=DEFAULT_CA)
Beispiel #35
0
 def __init__(self, api_key):
     self.handle = HTTPSConnectionPool(RapleafApi.HOST, timeout=RapleafApi.TIMEOUT)
     self.base_path = RapleafApi.BASE_PATH + '?api_key=%s' % (api_key)
Beispiel #36
0
class RapleafApi:
    
    HEADERS = {'User-Agent' : 'RapleafApi/Python/1.1'}
    BASE_PATH = '/v4/dr'
    HOST = 'personalize.rapleaf.com'
    TIMEOUT = 2.0
    
    def __init__(self, api_key):
        self.handle = HTTPSConnectionPool(RapleafApi.HOST, timeout=RapleafApi.TIMEOUT)
        self.base_path = RapleafApi.BASE_PATH + '?api_key=%s' % (api_key)
    
    def query_by_email(self, email, hash_email=False, show_available=False):
        """
        Takes an e-mail and returns a hash which maps attribute fields onto attributes
        If the hash_email option is set, then the email will be hashed before it's sent to Rapleaf.
        If the show_available option is set, then the string "Data Available" will be returned for 
        those fields which the API account is not subscribed but for which RapLeaf has data.
        """
        if hash_email:
            s = hashlib.sha1()
            s.update(email.lower())
            return self.query_by_sha1(s.hexdigest(), show_available)
        url = '%s&email=%s' % (self.base_path, quote(email))
        return self.__get_json_response(url, show_available)
    
    def query_by_md5(self, md5_email, show_available=False):
        """
        Takes an e-mail that has already been hashed by md5
        and returns a hash which maps attribute fields onto attributes.
        If the show_available option is set, then the string "Data Available" will be returned for 
        those fields which the API account is not subscribed but for which RapLeaf has data.
        """
        url = '%s&md5_email=%s' % (self.base_path, quote(md5_email))
        return self.__get_json_response(url, show_available)
    
    def query_by_sha1(self, sha1_email, show_available=False):
        """
        Takes an e-mail that has already been hashed by sha1
        and returns a hash which maps attribute fields onto attributes.
        If the show_available option is set, then the string "Data Available" will be returned for 
        those fields which the API account is not subscribed but for which RapLeaf has data.
        """
        url = '%s&sha1_email=%s' % (self.base_path, quote(sha1_email))
        return self.__get_json_response(url, show_available)
        
    def query_by_nap(self, first, last, street, city, state, email=None, show_available=False):
        """
        Takes first name, last name, and postal (street, city, and state acronym),
        and returns a hash which maps attribute fields onto attributes
        Though not necessary, adding an e-mail increases hit rate.
        If the show_available option is set, then the string "Data Available" will be returned for 
        those fields which the API account is not subscribed but for which RapLeaf has data.
        """
        url = '%s&first=%s&last=%s&street=%s&city=%s&state=%s' % (
            self.base_path, quote(first), quote(last), 
            quote(street), quote(city), quote(state))
        if email:
            url = '%s&email=%s' % (url, quote(email))
        return self.__get_json_response(url, show_available)
    
    def query_by_naz(self, first, last, zip4, email=None, show_available=False):
        """
        Takes first name, last name, and zip4 code (5-digit zip 
        and 4-digit extension separated by a dash as a string),
        and returns a hash which maps attribute fields onto attributes
        Though not necessary, adding an e-mail increases hit rate.
        If the show_available option is set, then the string "Data Available" will be returned for 
        those fields which the API account is not subscribed but for which RapLeaf has data.
        """
        url = '%s&first=%s&last=%s&zip4=%s' % (
            self.base_path, quote(first), quote(last), zip4)
        if email:
            url = '%s&email=%s' % (url, quote(email))
        return self.__get_json_response(url, show_available)
        
    def __get_json_response(self, path, show_available=False):
        """
        Pre: Path is an extension to personalize.rapleaf.com
        Note that an exception is raised if an HTTP response code
        other than 200 is sent back. In this case, both the error code
        the error code and error body are accessible from the exception raised
        """
        if show_available:
            path += '&show_available'
        json_response = self.handle.request('GET', path, headers=RapleafApi.HEADERS)
        if 200 <= json_response.status < 300:
            if json_response.data:
                return json.JSONDecoder().decode(json_response.data)
            else:
                return {}
        else:
            raise Exception(json_response.status, json_response.data.decode("utf-8"))
class HTTP(object):
    """ Wrapper for HTTP method calls.
    """
    @staticmethod
    def authorization(user, password):
        return 'Basic ' + b64encode(
            (user + ":" + password).encode("utf-8")).decode("ascii")

    def __init__(self, uri, headers, verified):
        self.uri = uri
        self.verified = verified
        parts = urlsplit(uri)
        scheme = parts.scheme
        host = parts.hostname
        port = parts.port
        if scheme == "http":
            from urllib3 import HTTPConnectionPool
            self._http = HTTPConnectionPool("%s:%d" % (host, port))
        elif scheme == "https":
            from urllib3 import HTTPSConnectionPool
            if verified:
                from certifi import where
                self._http = HTTPSConnectionPool("%s:%d" % (host, port),
                                                 cert_reqs="CERT_REQUIRED",
                                                 ca_certs=where())
            else:
                self._http = HTTPSConnectionPool("%s:%d" % (host, port))
        else:
            raise ValueError("Unsupported scheme %r" % scheme)
        self.path = parts.path
        if "auth" in headers:
            user, password = headers.pop("auth")
            headers["Authorization"] = 'Basic ' + b64encode(
                (ustr(user) + u":" +
                 ustr(password)).encode("utf-8")).decode("ascii")
        self.headers = headers

    def __del__(self):
        self.close()

    def __eq__(self, other):
        try:
            return self.uri == other.uri
        except AttributeError:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def request(self, method, url, fields=None, headers=None, **urlopen_kw):
        from neo4j.v1 import ServiceUnavailable
        from urllib3.exceptions import MaxRetryError
        try:
            if self.verified:
                return self._http.request(method, url, fields, headers,
                                          **urlopen_kw)
            else:
                with catch_warnings():
                    simplefilter("ignore")
                    return self._http.request(method, url, fields, headers,
                                              **urlopen_kw)
        except MaxRetryError:
            raise ServiceUnavailable("Cannot send %s request to <%s>" %
                                     (method, url))

    def get_json(self, ref):
        """ Perform an HTTP GET to this resource and return JSON.
        """
        rs = self.request("GET", self.path + ref, headers=self.headers)
        try:
            if rs.status == 200:
                return json_loads(rs.data.decode('utf-8'))
            else:
                self.raise_error(rs.status, rs.data)
        finally:
            rs.close()

    def post(self, ref, json, expected):
        """ Perform an HTTP POST to this resource.
        """
        headers = dict(self.headers)
        if json is not None:
            headers["Content-Type"] = "application/json"
            json = json_dumps(json).encode('utf-8')
        rs = self.request("POST",
                          self.path + ref,
                          headers=self.headers,
                          body=json)
        if rs.status not in expected:
            self.raise_error(rs.status, rs.data)
        return rs

    def delete(self, ref, expected):
        """ Perform an HTTP DELETE to this resource.
        """
        rs = self.request("DELETE", self.path + ref, headers=self.headers)
        if rs.status not in expected:
            self.raise_error(rs.status, rs.data)
        return rs

    def close(self):
        if self._http and self._http.pool:
            self._http.close()

    def raise_error(self, status_code, data):
        if status_code == UNAUTHORIZED:
            raise AuthError(self.uri)
        if status_code == FORBIDDEN:
            raise Forbidden(self.uri)
        if data:
            content = json_loads(data.decode('utf-8'))
        else:
            content = {}
        message = content.pop(
            "message",
            "HTTP request to <%s> returned unexpected status code %s" %
            (self.uri, status_code))
        error = GraphError(message, **content)
        error.http_status_code = status_code
        raise error
Beispiel #38
0
class TestHTTPS(HTTPSDummyServerTestCase):
    tls_protocol_name = None

    def setup_method(self, method):
        self._pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         ca_certs=DEFAULT_CA)

    def teardown_method(self, method):
        self._pool.close()

    def test_simple(self):
        r = self._pool.request("GET", "/")
        assert r.status == 200, r.data

    @fails_on_travis_gce
    def test_dotted_fqdn(self):
        with HTTPSConnectionPool(self.host + ".",
                                 self.port,
                                 ca_certs=DEFAULT_CA) as pool:
            r = pool.request("GET", "/")
            assert r.status == 200, r.data

    def test_client_intermediate(self):
        client_cert, client_key = (
            DEFAULT_CLIENT_CERTS["certfile"],
            DEFAULT_CLIENT_CERTS["keyfile"],
        )
        with HTTPSConnectionPool(
                self.host,
                self.port,
                key_file=client_key,
                cert_file=client_cert,
                ca_certs=DEFAULT_CA,
        ) as https_pool:
            r = https_pool.request("GET", "/certificate")
            subject = json.loads(r.data.decode("utf-8"))
            assert subject["organizationalUnitName"].startswith(
                "Testing server cert")

    def test_client_no_intermediate(self):
        client_cert, client_key = (
            DEFAULT_CLIENT_NO_INTERMEDIATE_CERTS["certfile"],
            DEFAULT_CLIENT_NO_INTERMEDIATE_CERTS["keyfile"],
        )
        with HTTPSConnectionPool(
                self.host,
                self.port,
                cert_file=client_cert,
                key_file=client_key,
                ca_certs=DEFAULT_CA,
        ) as https_pool:
            try:
                https_pool.request("GET", "/certificate", retries=False)
            except SSLError as e:
                if not ("alert unknown ca" in str(e)
                        or "invalid certificate chain" in str(e)
                        or "unknown Cert Authority" in str(e) or
                        # https://github.com/urllib3/urllib3/issues/1422
                        "connection closed via error" in str(e)
                        or "WSAECONNRESET" in str(e)):
                    raise
            except ProtocolError as e:
                if not ("An existing connection was forcibly closed by the remote host"
                        in str(e)
                        # Python 3.7.4+
                        or "WSAECONNRESET" in str(e)  # Windows
                        or "EPIPE" in str(e)  # macOS
                        ):
                    raise

    @requires_ssl_context_keyfile_password
    def test_client_key_password(self):
        client_cert, client_key = (
            DEFAULT_CLIENT_CERTS["certfile"],
            PASSWORD_CLIENT_KEYFILE,
        )
        with HTTPSConnectionPool(
                self.host,
                self.port,
                ca_certs=DEFAULT_CA,
                key_file=client_key,
                cert_file=client_cert,
                key_password="******",
        ) as https_pool:
            r = https_pool.request("GET", "/certificate")
            subject = json.loads(r.data.decode("utf-8"))
            assert subject["organizationalUnitName"].startswith(
                "Testing server cert")

    @requires_ssl_context_keyfile_password
    def test_client_encrypted_key_requires_password(self):
        client_cert, client_key = (
            DEFAULT_CLIENT_CERTS["certfile"],
            PASSWORD_CLIENT_KEYFILE,
        )
        with pytest.raises(SSLError) as e:
            HTTPSConnectionPool(
                self.host,
                self.port,
                key_file=client_key,
                cert_file=client_cert,
                key_password=None,
            )
        assert "password is required" in str(e.value)

    def test_verified(self):
        with HTTPSConnectionPool(self.host,
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            with mock.patch("warnings.warn") as warn:
                r = https_pool.request("GET", "/")
                assert r.status == 200

                # Modern versions of Python, or systems using PyOpenSSL, don't
                # emit warnings.
                if (sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL
                        or util.IS_SECURETRANSPORT):
                    assert not warn.called, warn.call_args_list
                else:
                    assert warn.called
                    if util.HAS_SNI:
                        call = warn.call_args_list[0]
                    else:
                        call = warn.call_args_list[1]
                    error = call[0][1]
                    assert error == InsecurePlatformWarning

    def test_verified_with_context(self):
        ctx = util.ssl_.create_urllib3_context(cert_reqs=ssl.CERT_REQUIRED)
        ctx.load_verify_locations(cafile=DEFAULT_CA)
        with HTTPSConnectionPool(self.host, self.port,
                                 ssl_context=ctx) as https_pool:
            with mock.patch("warnings.warn") as warn:
                r = https_pool.request("GET", "/")
                assert r.status == 200

                # Modern versions of Python, or systems using PyOpenSSL, don't
                # emit warnings.
                if (sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL
                        or util.IS_SECURETRANSPORT):
                    assert not warn.called, warn.call_args_list
                else:
                    assert warn.called
                    if util.HAS_SNI:
                        call = warn.call_args_list[0]
                    else:
                        call = warn.call_args_list[1]
                    error = call[0][1]
                    assert error == InsecurePlatformWarning

    def test_context_combines_with_ca_certs(self):
        ctx = util.ssl_.create_urllib3_context(cert_reqs=ssl.CERT_REQUIRED)
        with HTTPSConnectionPool(self.host,
                                 self.port,
                                 ca_certs=DEFAULT_CA,
                                 ssl_context=ctx) as https_pool:
            with mock.patch("warnings.warn") as warn:
                r = https_pool.request("GET", "/")
                assert r.status == 200

                # Modern versions of Python, or systems using PyOpenSSL, don't
                # emit warnings.
                if (sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL
                        or util.IS_SECURETRANSPORT):
                    assert not warn.called, warn.call_args_list
                else:
                    assert warn.called
                    if util.HAS_SNI:
                        call = warn.call_args_list[0]
                    else:
                        call = warn.call_args_list[1]
                    error = call[0][1]
                    assert error == InsecurePlatformWarning

    @onlyPy279OrNewer
    @notSecureTransport  # SecureTransport does not support cert directories
    @notOpenSSL098  # OpenSSL 0.9.8 does not support cert directories
    def test_ca_dir_verified(self):
        with HTTPSConnectionPool(self.host,
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_cert_dir=DEFAULT_CA_DIR) as https_pool:
            with mock.patch("warnings.warn") as warn:
                r = https_pool.request("GET", "/")
                assert r.status == 200
                assert not warn.called, warn.call_args_list

    def test_invalid_common_name(self):
        with HTTPSConnectionPool("127.0.0.1",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            with pytest.raises(MaxRetryError) as e:
                https_pool.request("GET", "/")
            assert isinstance(e.value.reason, SSLError)
            assert "doesn't match" in str(
                e.value.reason) or "certificate verify failed" in str(
                    e.value.reason)

    def test_verified_with_bad_ca_certs(self):
        with HTTPSConnectionPool(self.host,
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA_BAD) as https_pool:
            with pytest.raises(MaxRetryError) as e:
                https_pool.request("GET", "/")
            assert isinstance(e.value.reason, SSLError)
            assert "certificate verify failed" in str(e.value.reason), (
                "Expected 'certificate verify failed', instead got: %r" %
                e.value.reason)

    def test_verified_without_ca_certs(self):
        # default is cert_reqs=None which is ssl.CERT_NONE
        with HTTPSConnectionPool(self.host,
                                 self.port,
                                 cert_reqs="CERT_REQUIRED") as https_pool:
            with pytest.raises(MaxRetryError) as e:
                https_pool.request("GET", "/")
            assert isinstance(e.value.reason, SSLError)
            # there is a different error message depending on whether or
            # not pyopenssl is injected
            assert ("No root certificates specified" in str(e.value.reason)
                    # PyPy sometimes uses all-caps here
                    or
                    "certificate verify failed" in str(e.value.reason).lower()
                    or "invalid certificate chain" in str(e.value.reason)), (
                        "Expected 'No root certificates specified',  "
                        "'certificate verify failed', or "
                        "'invalid certificate chain', "
                        "instead got: %r" % e.value.reason)

    def test_unverified_ssl(self):
        """ Test that bare HTTPSConnection can connect, make requests """
        with HTTPSConnectionPool(self.host, self.port,
                                 cert_reqs=ssl.CERT_NONE) as pool:
            with mock.patch("warnings.warn") as warn:
                r = pool.request("GET", "/")
                assert r.status == 200
                assert warn.called

                # Modern versions of Python, or systems using PyOpenSSL, only emit
                # the unverified warning. Older systems may also emit other
                # warnings, which we want to ignore here.
                calls = warn.call_args_list
                assert InsecureRequestWarning in [x[0][1] for x in calls]

    def test_ssl_unverified_with_ca_certs(self):
        with HTTPSConnectionPool(self.host,
                                 self.port,
                                 cert_reqs="CERT_NONE",
                                 ca_certs=DEFAULT_CA_BAD) as pool:
            with mock.patch("warnings.warn") as warn:
                r = pool.request("GET", "/")
                assert r.status == 200
                assert warn.called

                # Modern versions of Python, or systems using PyOpenSSL, only emit
                # the unverified warning. Older systems may also emit other
                # warnings, which we want to ignore here.
                calls = warn.call_args_list
                if (sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL
                        or util.IS_SECURETRANSPORT):
                    category = calls[0][0][1]
                elif util.HAS_SNI:
                    category = calls[1][0][1]
                else:
                    category = calls[2][0][1]
                assert category == InsecureRequestWarning

    def test_assert_hostname_false(self):
        with HTTPSConnectionPool("localhost",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            https_pool.assert_hostname = False
            https_pool.request("GET", "/")

    def test_assert_specific_hostname(self):
        with HTTPSConnectionPool("localhost",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            https_pool.assert_hostname = "localhost"
            https_pool.request("GET", "/")

    def test_server_hostname(self):
        with HTTPSConnectionPool(
                "127.0.0.1",
                self.port,
                cert_reqs="CERT_REQUIRED",
                ca_certs=DEFAULT_CA,
                server_hostname="localhost",
        ) as https_pool:
            conn = https_pool._new_conn()
            https_pool._start_conn(conn, connect_timeout=None)

            # Assert the wrapping socket is using the passed-through SNI name.
            # pyopenssl doesn't let you pull the server_hostname back off the
            # socket, so only add this assertion if the attribute is there (i.e.
            # the python ssl module).
            # XXX This is highly-specific to SyncBackend
            # See https://github.com/python-trio/urllib3/pull/54#discussion_r241683895
            # for potential solutions
            sock = conn._sock._sock
            if hasattr(sock, "server_hostname"):
                assert sock.server_hostname == "localhost"

    def test_assert_fingerprint_md5(self):
        with HTTPSConnectionPool("localhost",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            https_pool.assert_fingerprint = (
                "F2:06:5A:42:10:3F:45:1C:17:FE:E6:07:1E:8A:86:E5")

            https_pool.request("GET", "/")

    def test_assert_fingerprint_sha1(self):
        with HTTPSConnectionPool("localhost",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            https_pool.assert_fingerprint = (
                "92:81:FE:85:F7:0C:26:60:EC:D6:B3:BF:93:CF:F9:71:CC:07:7D:0A")
            https_pool.request("GET", "/")

    def test_assert_fingerprint_sha256(self):
        with HTTPSConnectionPool("localhost",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            https_pool.assert_fingerprint = (
                "C5:4D:0B:83:84:89:2E:AE:B4:58:BB:12:"
                "F7:A6:C4:76:05:03:88:D8:57:65:51:F3:"
                "1E:60:B0:8B:70:18:64:E6")
            https_pool.request("GET", "/")

    def test_assert_invalid_fingerprint(self):
        with HTTPSConnectionPool("127.0.0.1",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            https_pool.assert_fingerprint = (
                "AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA")

            def _test_request(pool):
                with pytest.raises(MaxRetryError) as cm:
                    pool.request("GET", "/", retries=0)
                assert isinstance(cm.value.reason, SSLError)

            _test_request(https_pool)
            https_pool._get_conn()

            # Uneven length
            https_pool.assert_fingerprint = "AA:A"
            _test_request(https_pool)
            https_pool._get_conn()

            # Invalid length
            https_pool.assert_fingerprint = "AA"
            _test_request(https_pool)

    def test_verify_none_and_bad_fingerprint(self):
        with HTTPSConnectionPool("127.0.0.1",
                                 self.port,
                                 cert_reqs="CERT_NONE",
                                 ca_certs=DEFAULT_CA_BAD) as https_pool:
            https_pool.assert_fingerprint = (
                "AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA")
            with pytest.raises(MaxRetryError) as cm:
                https_pool.request("GET", "/", retries=0)
            assert isinstance(cm.value.reason, SSLError)

    def test_verify_none_and_good_fingerprint(self):
        with HTTPSConnectionPool("127.0.0.1",
                                 self.port,
                                 cert_reqs="CERT_NONE",
                                 ca_certs=DEFAULT_CA_BAD) as https_pool:
            https_pool.assert_fingerprint = (
                "92:81:FE:85:F7:0C:26:60:EC:D6:B3:BF:93:CF:F9:71:CC:07:7D:0A")
            https_pool.request("GET", "/")

    @notSecureTransport
    def test_good_fingerprint_and_hostname_mismatch(self):
        # This test doesn't run with SecureTransport because we don't turn off
        # hostname validation without turning off all validation, which this
        # test doesn't do (deliberately). We should revisit this if we make
        # new decisions.
        with HTTPSConnectionPool("127.0.0.1",
                                 self.port,
                                 cert_reqs="CERT_REQUIRED",
                                 ca_certs=DEFAULT_CA) as https_pool:
            https_pool.assert_fingerprint = (
                "92:81:FE:85:F7:0C:26:60:EC:D6:B3:BF:93:CF:F9:71:CC:07:7D:0A")
            https_pool.request("GET", "/")

    @requires_network
    def test_https_timeout(self):

        timeout = Timeout(total=None, connect=SHORT_TIMEOUT)
        with HTTPSConnectionPool(
                TARPIT_HOST,
                self.port,
                timeout=timeout,
                retries=False,
                cert_reqs="CERT_REQUIRED",
        ) as https_pool:
            with pytest.raises(ConnectTimeoutError):
                https_pool.request("GET", "/")

        timeout = Timeout(read=0.01)
        with HTTPSConnectionPool(
                self.host,
                self.port,
                timeout=timeout,
                retries=False,
                cert_reqs="CERT_REQUIRED",
        ) as https_pool:
            https_pool.assert_fingerprint = (
                "92:81:FE:85:F7:0C:26:60:EC:D6:B3:BF:93:CF:F9:71:CC:07:7D:0A")

        timeout = Timeout(total=None)
        with HTTPSConnectionPool(self.host,
                                 self.port,
                                 timeout=timeout,
                                 cert_reqs="CERT_NONE") as https_pool:
            https_pool.request("GET", "/")

    @requires_network
    def test_enhanced_timeout(self):
        with HTTPSConnectionPool(
                TARPIT_HOST,
                self.port,
                timeout=Timeout(connect=SHORT_TIMEOUT),
                retries=False,
                cert_reqs="CERT_REQUIRED",
        ) as https_pool:
            conn = https_pool._new_conn()
            with pytest.raises(ConnectTimeoutError):
                https_pool.request("GET", "/")
            with pytest.raises(ConnectTimeoutError):
                https_pool._make_request(conn, "GET", "/")

        with HTTPSConnectionPool(
                TARPIT_HOST,
                self.port,
                timeout=Timeout(connect=LONG_TIMEOUT),
                retries=False,
                cert_reqs="CERT_REQUIRED",
        ) as https_pool:
            with pytest.raises(ConnectTimeoutError):
                https_pool.request("GET",
                                   "/",
                                   timeout=Timeout(connect=SHORT_TIMEOUT))

        with HTTPSConnectionPool(
                TARPIT_HOST,
                self.port,
                timeout=Timeout(total=None),
                retries=False,
                cert_reqs="CERT_REQUIRED",
        ) as https_pool:
            conn = https_pool._new_conn()
            try:
                with pytest.raises(ConnectTimeoutError):
                    https_pool.request("GET",
                                       "/",
                                       timeout=Timeout(total=None,
                                                       connect=SHORT_TIMEOUT))
            finally:
                conn.close()

    def test_enhanced_ssl_connection(self):
        fingerprint = "92:81:FE:85:F7:0C:26:60:EC:D6:B3:BF:93:CF:F9:71:CC:07:7D:0A"

        with HTTPSConnectionPool(
                self.host,
                self.port,
                cert_reqs="CERT_REQUIRED",
                ca_certs=DEFAULT_CA,
                assert_fingerprint=fingerprint,
        ) as https_pool:
            r = https_pool.urlopen("GET", "/")
            assert r.status == 200

    @onlyPy279OrNewer
    def test_ssl_correct_system_time(self):
        w = self._request_without_resource_warnings("GET", "/")
        assert [] == w

    @onlyPy279OrNewer
    def test_ssl_wrong_system_time(self):
        with mock.patch("urllib3._sync.connection.datetime") as mock_date:
            mock_date.date.today.return_value = datetime.date(1970, 1, 1)

            w = self._request_without_resource_warnings("GET", "/")

            assert len(w) == 1
            warning = w[0]

            assert SystemTimeWarning == warning.category
            assert str(RECENT_DATE) in warning.message.args[0]

    def _request_without_resource_warnings(self, method, url):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            with HTTPSConnectionPool(self.host,
                                     self.port,
                                     cert_reqs="CERT_REQUIRED",
                                     ca_certs=DEFAULT_CA) as pool:
                pool.request(method, url)

        return [x for x in w if not isinstance(x.message, ResourceWarning)]

    def test_set_ssl_version_to_tls_version(self):
        if self.tls_protocol_name is None:
            pytest.skip("Skipping base test class")

        self._pool.ssl_version = self.certs["ssl_version"]
        r = self._pool.request("GET", "/")
        assert r.status == 200, r.data

    def test_set_cert_default_cert_required(self):
        pool = HTTPSConnectionPool(self.host, self.port, ca_certs=DEFAULT_CA)
        assert pool.ssl_context.verify_mode == ssl.CERT_REQUIRED

    def test_tls_protocol_name_of_socket(self):
        if self.tls_protocol_name is None:
            pytest.skip("Skipping base test class")

        conn = self._pool._get_conn()
        conn.connect(self._pool.ssl_context)

        if not hasattr(conn._sock, "_version"):
            pytest.skip("_version() not available in backend")

        assert conn._sock._version() == self.tls_protocol_name
Beispiel #39
0
    def test_https_timeout(self):
        timeout = Timeout(connect=0.001)
        https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')

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

        timeout = Timeout(read=0.001)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        url = '/sleep?seconds=0.005'
        self.assertRaises(ReadTimeoutError, https_pool.request, 'GET', url)

        timeout = Timeout(total=None)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_NONE')
        https_pool.request('GET', '/')
Beispiel #40
0
class TestHTTPS(HTTPSDummyServerTestCase):
    def setUp(self):
        self._pool = HTTPSConnectionPool(self.host, self.port)

    def test_simple(self):
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_set_ssl_version_to_tlsv1(self):
        self._pool.ssl_version = ssl.PROTOCOL_TLSv1
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_verified(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

        with mock.patch('warnings.warn') as warn:
            r = https_pool.request('GET', '/')
            self.assertEqual(r.status, 200)

            if sys.version_info >= (2, 7, 9):
                self.assertFalse(warn.called, warn.call_args_list)
            else:
                self.assertTrue(warn.called)
                call, = warn.call_args_list
                error = call[0][1]
                self.assertEqual(error, InsecurePlatformWarning)

    def test_invalid_common_name(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL invalid common name")
        except SSLError as e:
            self.assertTrue("doesn't match" in str(e))

    def test_verified_with_bad_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA_BAD)

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL error with bad CA certs")
        except SSLError as e:
            self.assertTrue(
                'certificate verify failed' in str(e),
                "Expected 'certificate verify failed',"
                "instead got: %r" % e)

    def test_verified_without_ca_certs(self):
        # default is cert_reqs=None which is ssl.CERT_NONE
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL error with no CA certs when"
                      "CERT_REQUIRED is set")
        except SSLError as e:
            # there is a different error message depending on whether or
            # not pyopenssl is injected
            self.assertTrue(
                'No root certificates specified' in str(e)
                or 'certificate verify failed' in str(e),
                "Expected 'No root certificates specified' or "
                "'certificate verify failed', "
                "instead got: %r" % e)

    def test_no_ssl(self):
        pool = HTTPSConnectionPool(self.host, self.port)
        pool.ConnectionCls = None
        self.assertRaises(SSLError, pool._new_conn)
        self.assertRaises(SSLError, pool.request, 'GET', '/')

    def test_unverified_ssl(self):
        """ Test that bare HTTPSConnection can connect, make requests """
        pool = HTTPSConnectionPool(self.host, self.port)
        pool.ConnectionCls = UnverifiedHTTPSConnection

        with mock.patch('warnings.warn') as warn:
            r = pool.request('GET', '/')
            self.assertEqual(r.status, 200)
            self.assertTrue(warn.called)

            call, = warn.call_args_list
            category = call[0][1]
            self.assertEqual(category, InsecureRequestWarning)

    def test_ssl_unverified_with_ca_certs(self):
        pool = HTTPSConnectionPool(self.host,
                                   self.port,
                                   cert_reqs='CERT_NONE',
                                   ca_certs=DEFAULT_CA_BAD)

        with mock.patch('warnings.warn') as warn:
            r = pool.request('GET', '/')
            self.assertEqual(r.status, 200)
            self.assertTrue(warn.called)

            calls = warn.call_args_list
            if sys.version_info >= (2, 7, 9):
                category = calls[0][0][1]
            else:
                category = calls[1][0][1]
            self.assertEqual(category, InsecureRequestWarning)

    @requires_network
    def test_ssl_verified_with_platform_ca_certs(self):
        """
        We should rely on the platform CA file to validate authenticity of SSL
        certificates. Since this file is used by many components of the OS,
        such as curl, apt-get, etc., we decided to not touch it, in order to
        not compromise the security of the OS running the test suite (typically
        urllib3 developer's OS).

        This test assumes that httpbin.org uses a certificate signed by a well
        known Certificate Authority.
        """
        try:
            import urllib3.contrib.pyopenssl
        except ImportError:
            raise SkipTest('Test requires PyOpenSSL')
        if (urllib3.connection.ssl_wrap_socket is
                urllib3.contrib.pyopenssl.orig_connection_ssl_wrap_socket):
            # Not patched
            raise SkipTest('Test should only be run after PyOpenSSL '
                           'monkey patching')

        https_pool = HTTPSConnectionPool('httpbin.org',
                                         443,
                                         cert_reqs=ssl.CERT_REQUIRED)

        https_pool.request('HEAD', '/')

    def test_assert_hostname_false(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_hostname = False
        https_pool.request('GET', '/')

    def test_assert_specific_hostname(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_hostname = 'localhost'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_md5(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = 'CA:84:E1:AD0E5a:ef:2f:C3:09' \
                                        ':E7:30:F8:CD:C8:5B'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_sha1(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_sha256(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = ('9A:29:9D:4F:47:85:1C:51:23:F5:9A:A3:'
                                         '0F:5A:EF:96:F9:2E:3C:22:2E:FC:E8:BC:'
                                         '0E:73:90:37:ED:3B:AA:AB')
        https_pool.request('GET', '/')

    def test_assert_invalid_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = 'AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:' \
                                        'AA:AA:AA:AA:AA:AA:AA:AA:AA'

        self.assertRaises(SSLError, https_pool.request, 'GET', '/')
        https_pool._get_conn()

        # Uneven length
        https_pool.assert_fingerprint = 'AA:A'
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')
        https_pool._get_conn()

        # Invalid length
        https_pool.assert_fingerprint = 'AA'
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')

    def test_verify_none_and_bad_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_NONE',
                                         ca_certs=DEFAULT_CA_BAD)

        https_pool.assert_fingerprint = 'AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:' \
                                        'AA:AA:AA:AA:AA:AA:AA:AA:AA'
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')

    def test_verify_none_and_good_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_NONE',
                                         ca_certs=DEFAULT_CA_BAD)

        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool.request('GET', '/')

    def test_good_fingerprint_and_hostname_mismatch(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)

        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool.request('GET', '/')

    @requires_network
    def test_https_timeout(self):
        timeout = Timeout(connect=0.001)
        https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')

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

        timeout = Timeout(read=0.001)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        url = '/sleep?seconds=0.005'
        self.assertRaises(ReadTimeoutError, https_pool.request, 'GET', url)

        timeout = Timeout(total=None)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_NONE')
        https_pool.request('GET', '/')

    def test_tunnel(self):
        """ test the _tunnel behavior """
        timeout = Timeout(total=None)
        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()
        https_pool._make_request(conn, 'GET', '/')
        conn._tunnel.assert_called_once_with()

    @onlyPy26OrOlder
    def test_tunnel_old_python(self):
        """HTTPSConnection can still make connections if _tunnel_host isn't set

        The _tunnel_host attribute was added in 2.6.3 - because our test runners
        generally use the latest Python 2.6, we simulate the old version by
        deleting the attribute from the HTTPSConnection.
        """
        conn = self._pool._new_conn()
        del conn._tunnel_host
        self._pool._make_request(conn, 'GET', '/')

    @requires_network
    def test_enhanced_timeout(self):
        def new_pool(timeout, cert_reqs='CERT_REQUIRED'):
            https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                             self.port,
                                             timeout=timeout,
                                             retries=False,
                                             cert_reqs=cert_reqs)
            return https_pool

        https_pool = new_pool(Timeout(connect=0.001))
        conn = https_pool._new_conn()
        self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/')
        self.assertRaises(ConnectTimeoutError, https_pool._make_request, conn,
                          'GET', '/')

        https_pool = new_pool(Timeout(connect=5))
        self.assertRaises(ConnectTimeoutError,
                          https_pool.request,
                          'GET',
                          '/',
                          timeout=Timeout(connect=0.001))

        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))

    def test_enhanced_ssl_connection(self):
        fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:7A:F2:8A:D7:1E:07:33:67:DE'

        conn = VerifiedHTTPSConnection(self.host, self.port)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA,
                                         assert_fingerprint=fingerprint)

        https_pool._make_request(conn, 'GET', '/')

    def test_ssl_correct_system_time(self):
        self._pool.cert_reqs = 'CERT_REQUIRED'
        self._pool.ca_certs = DEFAULT_CA

        w = self._request_without_resource_warnings('GET', '/')
        self.assertEqual([], w)

    def test_ssl_wrong_system_time(self):
        self._pool.cert_reqs = 'CERT_REQUIRED'
        self._pool.ca_certs = DEFAULT_CA
        with mock.patch('urllib3.connection.datetime') as mock_date:
            mock_date.date.today.return_value = datetime.date(1970, 1, 1)

            w = self._request_without_resource_warnings('GET', '/')

            self.assertEqual(len(w), 1)
            warning = w[0]

            self.assertEqual(SystemTimeWarning, warning.category)
            self.assertTrue(str(RECENT_DATE) in warning.message.args[0])

    def _request_without_resource_warnings(self, method, url):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            self._pool.request(method, url)

        return [x for x in w if not isinstance(x.message, ResourceWarning)]
Beispiel #41
0
class TestHTTPS(HTTPSDummyServerTestCase):
    def setUp(self):
        self._pool = HTTPSConnectionPool(self.host, self.port)

    def test_simple(self):
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_set_ssl_version_to_tlsv1(self):
        self._pool.ssl_version = ssl.PROTOCOL_TLSv1
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_verified(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

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

        https_pool.ca_certs = DEFAULT_CA_BAD

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL error with wrong CA")
        except SSLError as e:
            self.assertTrue(
                'certificate verify failed' in str(e),
                "Expected 'certificate verify failed',"
                "instead got: %r" % e)

        https_pool.ca_certs = DEFAULT_CA
        https_pool.request('GET', '/')  # Should succeed without exceptions.

        https_fail_pool = HTTPSConnectionPool('127.0.0.1',
                                              self.port,
                                              cert_reqs='CERT_REQUIRED')
        https_fail_pool.ca_certs = DEFAULT_CA

        try:
            https_fail_pool.request('GET', '/')
            self.fail("Didn't raise SSL invalid common name")
        except SSLError as e:
            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

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

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

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

    def test_cert_reqs_as_constant(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs=ssl.CERT_REQUIRED)

        https_pool.ca_certs = DEFAULT_CA_BAD
        # if we pass in an invalid value it defaults to CERT_NONE
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')

    def test_cert_reqs_as_short_string(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='REQUIRED')

        https_pool.ca_certs = DEFAULT_CA_BAD
        # if we pass in an invalid value it defaults to CERT_NONE
        self.assertRaises(SSLError, https_pool.request, 'GET', '/')

    def test_ssl_unverified_with_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_NONE')

        https_pool.ca_certs = DEFAULT_CA_BAD
        https_pool.request('GET', '/')

    def test_verified_without_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

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

    def test_invalid_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        # Empty string won't throw on py2
        https_pool.ca_certs = '/no_valid_path_to_ca_certs'

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

    def test_assert_specific_hostname(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_hostname = 'localhost'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_md5(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CA:84:E1:AD0E5a:ef:2f:C3:09' \
                                        ':E7:30:F8:CD:C8:5B'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_sha1(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'CC:45:6A:90:82:F7FF:C0:8218:8e:' \
                                        '7A:F2:8A:D7:1E:07:33:67:DE'
        https_pool.request('GET', '/')

    def test_assert_invalid_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = 'AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:' \
                                        'AA:AA:AA:AA:AA:AA:AA:AA:AA'

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

        # invalid length
        https_pool.assert_fingerprint = 'AA'

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

        # uneven length
        https_pool.assert_fingerprint = 'AA:A'

        self.assertRaises(SSLError, https_pool.request, 'GET', '/')
Beispiel #42
0
 def setUp(self):
     self._pool = HTTPSConnectionPool(self.host, self.port)
     self.addCleanup(self._pool.close)
class RapleafApi:
	apiKey = 'SET_ME'		# Set your API key here
		 
	headers = {'User-Agent' : 'RapleafApi/Python/1.1'}
	basePath = '/v4/dr?api_key=%s' %(apiKey)
	host = 'personalize.rapleaf.com'
	timeout = 2.0
	
	def __init__(self):
		self.handle = HTTPSConnectionPool(RapleafApi.host, timeout = RapleafApi.timeout)
	
	def query_by_email(self, email, hash_email = False):
		"""
		Takes an e-mail and returns a hash which maps attribute fields onto attributes
		If the hash_email option is set, then the email will be hashed before it's sent to Rapleaf
		"""
		if hash_email:
			s = hashlib.sha1()
			s.update(email)
			return self.query_by_sha1(s.digest())
		url = '%s&email=%s' % (RapleafApi.basePath, urllib.quote(email))
		return self.__get_json_response(url)
	
	def query_by_md5(self, md5_email):
		"""
		Takes an e-mail that has already been hashed by md5
		and returns a hash which maps attribute fields onto attributes
		"""
		url = '%s&md5_email=%s' % (RapleafApi.basePath, urllib.quote(md5_email))
		return self.__get_json_response(url)
	
	def query_by_sha1(self, sha1_email):
		"""
		Takes an e-mail that has already been hashed by sha1
		and returns a hash which maps attribute fields onto attributes
		"""
		url = '%s&sha1_email=%s' % (RapleafApi.basePath, urllib.quote(sha1_email))
		return self.__get_json_response(url)
		
	def query_by_nap(self, first, last, street, city, state, email = None):
		"""
		Takes first name, last name, and postal (street, city, and state acronym),
		and returns a hash which maps attribute fields onto attributes
		Though not necessary, adding an e-mail increases hit rate
		"""
		if email:
			url = '%s&email=%s&first=%s&last=%s&street=%s&city=%s&state=%s' % (RapleafApi.basePath, 
			urllib.quote(email), urllib.quote(first), urllib.quote(last), 
			urllib.quote(street), urllib.quote(city), urllib.quote(state))
		else:
			url = '%s&first=%s&last=%s&street=%s&city=%s&state=%s' % (RapleafApi.basePath, 
			urllib.quote(first), urllib.quote(last), 
			urllib.quote(street), urllib.quote(city), urllib.quote(state))
		return self.__get_json_response(url)
	
	def query_by_naz(self, first, last, zip4, email = None):
		"""
		Takes first name, last name, and zip4 code (5-digit zip 
		and 4-digit extension separated by a dash as a string),
		and returns a hash which maps attribute fields onto attributes
		Though not necessary, adding an e-mail increases hit rate
		"""
		if email:
			url = '%s&email=%s&first=%s&last=%s&zip4=%s' % (RapleafApi.basePath, 
			urllib.quote(email), urllib.quote(first), urllib.quote(last), zip4)
		else:
			url = '%s&first=%s&last=%s&zip4=%s' % (RapleafApi.basePath, 
			urllib.quote(first), urllib.quote(last), zip4)
		return self.__get_json_response(url)
		
	def __get_json_response(self, path):
		"""
		Pre: Path is an extension to personalize.rapleaf.com
    Note that an exception is raised if an HTTP response code
    other than 200 is sent back. In this case, both the error code
    the error code and error body are accessible from the exception raised
		"""
		json_response = self.handle.get_url(path, headers = RapleafApi.headers)
		if 200 <= json_response.status < 300:
			if json_response.data:
				return json.JSONDecoder().decode(json_response.data)
			else:
				return {}
		else:
			raise Exception(json_response.status, json_response.data)
Beispiel #44
0
 def test_assert_specific_hostname(self):
     with HTTPSConnectionPool(
         "localhost", self.port, cert_reqs="CERT_REQUIRED", ca_certs=DEFAULT_CA
     ) as https_pool:
         https_pool.assert_hostname = "localhost"
         https_pool.request("GET", "/")
Beispiel #45
0
 def test_dotted_fqdn(self):
     pool = HTTPSConnectionPool(self.host + '.', self.port)
     r = pool.request('GET', '/')
     self.assertEqual(r.status, 200, r.data)
Beispiel #46
0
 def setUp(self):
     self._pool = HTTPSConnectionPool(self.host, self.port, ca_certs=DEFAULT_CA)
     self.addCleanup(self._pool.close)
class HandleInfrastructure(DOInfrastructure):
    """
    Specialization of the general Digital Object Infrastructure based on the Handle System.
    Connects to the Handle System via a RESTful interface.
    """ 
    
    
    def __init__(self, host, port, user, user_index, password, path, prefix = None, additional_identifier_element = None, unsafe_ssl=False):
        '''
        Constructor.

        :param host: Host name of the Handle server.
        :param port: Port for the Handle server, usually 443 or 8443.
        :param user: Full user Handle name to authenticate for administrative permissions for modifying Handles. The full user name will usually be of the form 'prefix/suffix'.
        :param user_index: The index to use within the user Handle.
        :param password: The user password.
        :param path: The server path for the API endpoint, e.g. 'api/handles'. Does not include the host name.
        :param prefix: The Handle prefix to use (without trailing slash). If not given, all operations will work
          nonetheless, except for random handle creation. Note that setting a prefix does not mean that identifier 
          strings can omit it - all identifiers must ALWAYS include the prefix, no matter what.
        :param additional_identifier_element: A string that is inserted inbetween Handle prefix and suffix, e.g. if set
          to "test-", 10876/identifier becomes 10876/test-identifier.
        :unsafe_ssl: If set to True, SSL certificate warnings will be ignored. Do not activate this in productive environments!
        '''
        super(HandleInfrastructure, self).__init__()
        self._host = host
        self._port = port
        self._path = path
        self._prefix = prefix
        if unsafe_ssl:
            disable_warnings()
            self.__connpool = HTTPSConnectionPool(host, port=port, assert_hostname=False, cert_reqs="CERT_NONE")
        else:
            self.__connpool = HTTPSConnectionPool(host, port=port)
        self.__user_handle = prefix+"/"+user
        self.__user_index = user_index
        self.__authstring = b64encode(user_index+"%3A"+user+":"+password)
        self.__http_headers = {"Content-Type": "application/json", "Authorization": "Basic %s" % self.__authstring}
        if not self._path.endswith("/"):
            self._path = self._path + "/"
        self._additional_identifier_element = additional_identifier_element
            
    def _generate_random_identifier(self):
        if not self._prefix:
            raise ValueError("Cannot generate random Handles if no _prefix is provided!")
        rid = super(HandleInfrastructure, self)._generate_random_identifier()
        return self._prefix+"/"+rid

    def _prepare_identifier(self, identifier):
        # check identifier string for validity
        if " " in identifier:
            raise ValueError("Illegal Handle identifier string character; spaces are not supported! (identifier: %s)" % identifier)
        identifier = identifier.strip()
        if (self._additional_identifier_element):
            # split identifier into _prefix and suffix, insert additional element inbetween 
            parts = identifier.split("/", 1)
            if len(parts) != 2:
                raise ValueError("Invalid identifier - no separating slash between _prefix and suffix: %s" % identifier)
            if (parts[1].startswith(self._additional_identifier_element)):
                return self._path+identifier, identifier
            newident = parts[0]+"/"+self._additional_identifier_element+parts[1]
            return self._path+newident, newident
        else:
            return self._path+identifier, identifier
    
    def __generate_admin_value(self):
        return {"index":100,"type":"HS_ADMIN","data":{"format":"admin","value":{"handle":self.__user_handle,"index":self.__user_index,"permissions":"011111110011"}}}
    
    def _acquire_pid(self, identifier):
        path, identifier_prep = self._prepare_identifier(identifier)
        # Try to create Handle, but do not ovewrite existing
        values = {"values": [self.__generate_admin_value()]}
        resp = self.__connpool.urlopen("PUT", path+"?overwrite=false", str(values), self.__http_headers)
        # status check; 409 = Conflict on existing Handle
        if (resp.status == 409):
            raise PIDAlreadyExistsError("Handle already exists: %s" % identifier_prep)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not create Handle %s: %s" % (identifier_prep, resp.reason))
        return identifier_prep
    
    def _do_from_json(self, piddata, identifier, aliases):
        """
        Construct a DO instance from given JSON data.
        
        :param piddata: JSON loaded data.
        :param identifier: Identifier of the DO.
        :param aliases: A list of aliases that were used to get to this identifier (may be empty). The list must be
          ordered in the order of alias resolution, i.e. aliases[0] pointed to aliases[1] etc. The last entry pointed 
          to the actual identifier. 
        :returns: A fully fledged DigitalObject instance
        """
        # piddata is an array of dicts, where each dict has keys: index, type, data
        references = {}
        res_type = None
        if not "values" in piddata:
            raise IOError("Illegal format of JSON response from Handle server: 'values' not found in JSON record!")
        for ele in piddata["values"]:
            idx = int(ele["index"])
            if idx == 2:
                res_type = ele["data"]["value"]
                continue
            if ele["type"] == "HS_ADMIN":
                # ignore HS_ADMIN values; these are taken care of by the REST service server-side
                continue
            # no special circumstances --> assign to annotations or references
            if REFERENCE_INDEX_END >= idx >= REFERENCE_INDEX_START:
                # reference; first, parse element data using json to a list
                list_data = json.loads(ele["data"]["value"])
                if not isinstance(list_data, list):
                    raise IOError("Illegal format of JSON response from Handle server: Cannot load reference list! Input: %s" % ele["data"])
                if ele["type"] not in references:
                    references[ele["type"]] = list_data
                else:
                    references[ele["type"]].extend(list_data)
                continue
        # create special instances for special resource types
        if res_type == DigitalObjectSet.RESOURCE_TYPE:
            return DigitalObjectSet(self, identifier, references=references, alias_identifiers=aliases)
        if res_type == DigitalObjectArray.RESOURCE_TYPE:
            return DigitalObjectArray(self, identifier, references=references, alias_identifiers=aliases)
        if res_type == DigitalObjectLinkedList.RESOURCE_TYPE:
            return DigitalObjectLinkedList(self, identifier, references=references, alias_identifiers=aliases)
        return DigitalObject(self, identifier, references, alias_identifiers=aliases)
        
    def lookup_pid(self, identifier):
        aliases = []
        while True:
            path, identifier = self._prepare_identifier(identifier)
            resp = self.__connpool.request("GET", path, None, self.__http_headers)
            if resp.status == 404:
                # Handle not found
                if len(aliases) > 0:
                    raise PIDAliasBrokenError("Alias %s does not exist. Already resolved aliases: %s" % (identifier, aliases))
                return None
            elif not(200 <= resp.status <= 299):
                raise IOError("Failed to look up Handle %s due to the following reason (HTTP Code %s): %s" % (identifier, resp.status, resp.reason))
            else:
                # check for HS_ALIAS redirect
                piddata = json.loads(resp.data)
                isa, alias_id = self._check_json_for_alias(piddata)
                if isa:
                    # write down alias identifier and redo lookup with target identifier
                    aliases.append(identifier)
                    identifier = alias_id
                    continue                    
                dobj = self._do_from_json(piddata, identifier, aliases)
                return dobj            
        
    def _determine_index(self, identifier, handledata, key, index_start, index_end=None):
        """
        Finds an index in the Handle key-metadata record to store a value for the given key. If the key is already
        present, its index will be reused. If it is not present, a free index will be determined. 
        
        :param identifier: The current Handle.
        :param key: The key that will be assigned.
        :param index_start: At which index the search should start.
        :param index_end: Where should the search end? Use None to search all indices greater than the start index.
        :raises: :exc:`IndexError` if all possible indices are already taken by other keys.
        :returns: an index value. 
        """
        matching_values = []
        free_index = index_start
        taken_indices = []        
        if not "values" in handledata:
            raise IOError("Illegal format of JSON response from Handle server: 'values' not found in JSON record!")
        for ele in handledata["values"]:
            idx = int(ele["index"])
            if (index_end and (index_start <= idx <= index_end))\
            or (not index_end and (index_start <= idx)):
                taken_indices.append(idx)
            if ele["type"] == key:
                matching_values.append(ele)
        if len(matching_values) > 1:
            raise IllegalHandleStructureError("Handle %s contains more than one entry of type %s!" % (identifier, key))
        elif len(matching_values) == 1:
            return int(matching_values[0]["index"])
        else:
            # key not present in Handle; must assign a new index
            # check for free index within bounds
            if taken_indices == []:
                return index_start
            m = min(taken_indices)
            if m == index_end:
                raise IllegalHandleStructureError("Handle %s does not have any more available index slots between %s and %s!" % (index_start, index_end))
            return m
        
    def _write_pid_value(self, identifier, index, valuetype, value):
        """
        Writes a single (index, type, value) to the Handle with given identifier.
        
        :param identifier: The Handle identifier.
        :param index: Index (positive 32 bit int).
        :param valuetype: Type (arbitrary)
        :param value: Value (arbitrary)
        """
        path, identifier = self._prepare_identifier(identifier)
        if type(index) is not int:
            raise ValueError("Index must be an integer! (was: type %s, value %s)" % (type(index), index))
        # write the raw (index, type, value) triple
        data = json.dumps([{"index": index, "type": valuetype, "data": {"format": "string", "value": value}}])
        resp = self.__connpool.urlopen("PUT", path+"?index=various", data, self.__http_headers)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not write raw value to Handle %s: %s" % (identifier, resp.reason))
    
    def _read_pid_value(self, identifier, index):
        """
        Reads a single indexed type and value from the Handle with given identifier.
        
        :returns: A tuple (type, value) or None if the given index is unassigned.
        :raises: :exc:`IOError` if no Handle with given identifier exists. 
        """
        path, identifier = self._prepare_identifier(str(identifier))
        if type(index) is not int:
            raise ValueError("Index must be an integer! (was: type %s, value %s)" % (type(index), index))
        # read only the given index
        resp = self.__connpool.request("GET", path+"?index=%s" % index, "", self.__http_headers)
        if resp.status == 404:
            # value not found; the Handle may exist, but the index is unused
            return None        
        if not(200 <= resp.status <= 299):
            raise IOError("Could not read raw value from Handle %s: %s" % (identifier, resp.reason))
        respdata = json.loads(resp.data)
        if not "values" in respdata:
            raise IOError("Illegal format of JSON response from Handle server: 'values' not found in JSON record!")
        for ele in respdata["values"]:
            if int(ele["index"]) == index:
                return (ele["type"], ele["data"]["value"])
        return None
        
    def _remove_pid_value(self, identifier, index):
        """
        Removes a single Handle value at Handle of given identifier at given index.
        
        :raises: :exc:`IOError` if no Handle with given identifier exists. 
        """
        path, identifier = self._prepare_identifier(str(identifier))
        if type(index) is not int:
            raise ValueError("Index must be an integer! (was: type %s, value %s)" % (type(index), index))
        # read only the given index
        resp = self.__connpool.urlopen("DELETE", path+"?index=%s" % index, "", self.__http_headers)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not remove raw value from Handle %s: %s" % (identifier, resp.reason))

    def _read_all_pid_values(self, identifier):
        """
        Reads the full Handle record of given identifier.
        
        :return: a dict with indexes as keys and (type, value) tuples as values.
        """
        path, identifier = self._prepare_identifier(identifier)
        # read full record
        resp = self.__connpool.request("GET", path, "", self.__http_headers)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not read raw values from Handle %s: %s" % (identifier, resp.reason))
        respdata = json.loads(resp.data)
        res = {}
        if not "values" in respdata:
            raise IOError("Illegal format of JSON response from Handle server: 'values' not found in JSON record!")
        for ele in respdata["values"]:
            res[int(ele["index"])] = (ele["type"], ele["data"]["value"])
        return res
    
    def _write_resource_information(self, identifier, resource_location, resource_type=None):
        path, identifier = self._prepare_identifier(identifier)
        handle_values = []
        if resource_location:
            handle_values = [{"index": INDEX_RESOURCE_LOCATION, "type": "URL", "data": {"format": "string", "value": resource_location}}]
        if resource_type:
            handle_values.append({"index": INDEX_RESOURCE_TYPE, "type": "", "data": {"format": "string", "value": resource_type}})
        data = json.dumps(handle_values)
        resp = self.__connpool.urlopen("PUT", path, data, self.__http_headers)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not write resource location to Handle %s: %s" % (identifier, resp.reason))

    def delete_do(self, identifier):
        path, identifier = self._prepare_identifier(identifier)
        resp = self.__connpool.urlopen("DELETE", path, headers=self.__http_headers)
        if resp.status == 404:
            raise KeyError("Handle not found: %s" % identifier)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not delete Handle %s: %s" % (identifier, resp.reason))

    def _write_reference(self, identifier, key, reference):
        path, identifier = self._prepare_identifier(identifier)
        # first, we need to determine the index to use by looking at the key
        resp = self.__connpool.request("GET", path, headers=self.__http_headers)
        if not(200 <= resp.status <= 299):
            raise IOError("Unknown Handle: %s" % identifier)
        dodata = json.loads(resp.data)
        index = self._determine_index(identifier, dodata, key, REFERENCE_INDEX_START, REFERENCE_INDEX_END)
        # now we can write the reference; note that reference may be a list. But this is okay, we
        # convert it to a string and take care of reconversion in the JSON-to-DO method
        reference_s = json.dumps(reference)
        data = json.dumps({"values": [{"index": index, "type": key, "data": {"format": "string", "value": reference_s}}]})
        resp = self.__connpool.urlopen("PUT", path+"?index=various", data, self.__http_headers)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not write references to Handle %s: %s" % (identifier, resp.reason))
            
            
    def create_alias(self, original, alias_identifier):
        if isinstance(original, DigitalObject):
            original_identifier = original.identifier
        else: 
            original_identifier = str(original)
        path, identifier = self._prepare_identifier(alias_identifier)
        # check for existing Handle
        resp = self.__connpool.request("GET", path, None, self.__http_headers)
        if (resp.status == 200):
            # Handle already exists
            raise PIDAlreadyExistsError("Handle already exists, cannot use it as an alias: %s" % identifier)
        if (resp.status != 404):
            raise IOError("Failed to check for existing Handle %s (HTTP Code %s): %s" % (identifier, resp.status, resp.reason))
        # okay, alias is available. Now create it.
        values = {"values": [self.__generate_admin_value(), {"index": 1, "type": "HS_ALIAS", "data": {"format": "string", "value": str(original_identifier)}}]}
        resp = self.__connpool.urlopen("PUT", path, str(values), self.__http_headers)
        if not(200 <= resp.status <= 299):
            raise IOError("Could not create Alias Handle %s: %s" % (identifier, resp.reason))
        return identifier
    
    def delete_alias(self, alias_identifier):
        # resolve to check if this is really an alias
        isa = self.is_alias(alias_identifier)
        if not isa:
            return False
        self.delete_do(alias_identifier)
        return True
    
    def is_alias(self, alias_identifier):
        path, identifier = self._prepare_identifier(alias_identifier)
        resp = self.__connpool.request("GET", path, None, self.__http_headers)
        if resp.status == 404:
            raise KeyError("Handle not found: %s" % identifier)
        if not(200 <= resp.status <= 299):
            raise IOError("Failed to lookup Handle %s for alias check: %s" % (identifier, resp.reason))
        # parse JSON, but do not create a Digital Object instance, as this might cause inefficient subsequent calls
        isa, a_id = self._check_json_for_alias(json.loads(resp.data))
        return isa

    def _check_json_for_alias(self, piddata):
        """
        Checks the given JSON data structure for presence of an HS_ALIAS marker.
        
        :returns: a tuple (b, id) where b is True or False and if b is True, id is the Handle string of the target
          Handle.
        """
        res = (False, None)
        for ele in piddata["values"]:
            if ele["type"] == "HS_ALIAS":
                res = (True, ele["data"]["value"])
                break
        return res        

    def prefix_pid(self, suffix):
        """
        Prepends a given (incomplete) identifier with the current Handle _prefix.
        """
        return self._prefix + "/" + suffix
    
    def manufacture_hashmap(self, identifier, characteristic_segment_number):
        """
        Factory method. Constructs Handle-based Hashmap implementation objects.
        
        :identifier: The PID of the record that should hold the hash map.
        :param: characteristic_segment_number: Since there can be multiple hash maps in a single record, this number
          is used to separate them from each other. 
        """
        return HandleHashmapImpl(self, identifier, characteristic_segment_number)
Beispiel #48
0
 def test_no_ssl(self):
     pool = HTTPSConnectionPool(self.host, self.port)
     pool.ConnectionCls = None
     self.assertRaises(SSLError, pool._new_conn)
     self.assertRaises(SSLError, pool.request, 'GET', '/')
Beispiel #49
0
    def test_verified(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

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

        https_pool.ca_certs = DEFAULT_CA_BAD

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL error with wrong CA")
        except SSLError as e:
            self.assertTrue(
                'certificate verify failed' in str(e),
                "Expected 'certificate verify failed',"
                "instead got: %r" % e)

        https_pool.ca_certs = DEFAULT_CA
        https_pool.request('GET', '/')  # Should succeed without exceptions.

        https_fail_pool = HTTPSConnectionPool('127.0.0.1',
                                              self.port,
                                              cert_reqs='CERT_REQUIRED')
        https_fail_pool.ca_certs = DEFAULT_CA

        try:
            https_fail_pool.request('GET', '/')
            self.fail("Didn't raise SSL invalid common name")
        except SSLError as e:
            self.assertTrue("doesn't match" in str(e))
Beispiel #50
0
 def test_dotted_fqdn(self):
     with HTTPSConnectionPool(self.host + ".",
                              self.port,
                              ca_certs=DEFAULT_CA) as pool:
         r = pool.request("GET", "/")
         assert r.status == 200, r.data
Beispiel #51
0
    def test_verified_without_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')

        self.assertRaises(SSLError, https_pool.request, 'GET', '/')
Beispiel #52
0
 def setUp(self):
     self._pool = HTTPSConnectionPool(self.host, self.port)
Beispiel #53
0
 def _test_request(pool: HTTPSConnectionPool) -> SSLError:
     with pytest.raises(MaxRetryError) as cm:
         pool.request("GET", "/", retries=0)
     assert isinstance(cm.value.reason, SSLError)
     return cm.value.reason
Beispiel #54
0
class Client:
    '''
    A fast, lightweight Python interface to the AlphaVantage API

    Usage:
        client = Client()
        client.get_quote("MSFT")

    '''
    def __init__(self):
        self.session = HTTPSConnectionPool(API_HOST)
        self.ThreadedRequest = ThreadedRequest

    def get_quote(self, symbol):
        '''
        fetch realtime quote for a stock

        :param symbol: a stock symbol
        :returns: realtime quote of the given symbol in json format

        '''

        payload = {
            "function": "GLOBAL_QUOTE",
            "symbol": symbol,
            "apikey": API_KEY
        }
        r = self.session.request('GET', '/query', fields=payload)
        return json.loads(r.data)

    def get_quotes(self, symbols):
        '''
        threaded request for fetching multiple realtime stock quotes

        :param symbols: a list of stock symbols
        :returns: list of stock quotes in json format

        '''

        results = ThreadedRequest(symbols, self.get_quote)

        return results

    def get_historical_quote(self, symbol, interval, output_size="full"):
        '''
        fetch historical data for a stock

        :param symbol: a stock symbol
        :param interval: aggragated time period for historical data (1min, 5min, 15min, 30min, 60min)
        :param output_size: ammount of data returned (full, compact)
        :returns: a list of historical stock quotes in json format

        '''

        assert interval in INTERVALS
        assert output_size in OUTPUT_SIZE

        payload = {
            "function": "TIME_SERIES_INTRADAY",
            "symbol": symbol,
            "interval": interval,
            "outputsize": output_size,
            "apikey": API_KEY
        }

        r = self.session.request('GET', '/query', fields=payload)
        return json.loads(r.data)

    def get_historical_quotes(self, symbols, interval, output_size="full"):
        '''
        threaded request for fetching multiple historical stock quotes

        :param symbols: a list of stock symbols
        :param interval: aggragated time period for historical data (1min, 5min, 15min, 30min, 60min)
        :param output_size: ammount of data returned (full, compact)
        :returns: a list of multiple historical stock quotes in json format

        '''

        results = ThreadedRequest(
            symbols, lambda symbol: self.get_historical_quote(
                symbol, interval, output_size))

        return results

    def get_sector_preformances(self):
        '''
        fetch realtime sector preformance
        
        :returns: list of sector preformances in json format

        '''

        payload = {"function": "SECTOR", "apikey": API_KEY}

        r = self.session.request('GET', '/query', fields=payload)
        return json.loads(r.data)
Beispiel #55
0
 def test_set_cert_default_cert_required(self):
     pool = HTTPSConnectionPool(self.host, self.port, ca_certs=DEFAULT_CA)
     assert pool.ssl_context.verify_mode == ssl.CERT_REQUIRED
Beispiel #56
0
 def __init__(self):
     self.session = HTTPSConnectionPool(API_HOST)
     self.ThreadedRequest = ThreadedRequest
Beispiel #57
0
    def test_https_timeout(self):
        timeout = Timeout(connect=0.001)
        https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        self.addCleanup(https_pool.close)

        timeout = Timeout(total=None, connect=0.001)
        https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        self.addCleanup(https_pool.close)
        self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/')

        timeout = Timeout(read=0.001)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        self.addCleanup(https_pool.close)
        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'

        timeout = Timeout(total=None)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_NONE')
        self.addCleanup(https_pool.close)
        https_pool.request('GET', '/')
Beispiel #58
0
class TowerDataApi:
    
    HEADERS = {'User-Agent' : 'TowerDataApi/Python/1.2'}
    POST_HEADERS = HEADERS.copy()
    POST_HEADERS.update({
        'Content-Type': 'application/json'
    })
    BASE_PATH = '/v5/td'
    EV_BASE_PATH = '/v5/ev'
    EPPEND_BASE_PATH = '/v5/eppend'
    BULK_BASE_PATH = '/v5/ei/bulk'
    HOST = 'api.towerdata.com'
    TIMEOUT = 30.0
    
    def __init__(self, api_key):
        self.handle = HTTPSConnectionPool(TowerDataApi.HOST, timeout=TowerDataApi.TIMEOUT)
        self.base_path = TowerDataApi.BASE_PATH + '?api_key=%s' % (api_key)
        self.base_ev_path = TowerDataApi.EV_BASE_PATH + '?api_key=%s' % (api_key)
        self.base_eppend_path = TowerDataApi.EPPEND_BASE_PATH + '?api_key=%s' % (api_key)
        self.base_bulk_path = (TowerDataApi.BULK_BASE_PATH +
                               '?api_key=%s' % (api_key))

    def validate_email(self, email, timeout=None):
        """
        Takes an e-mail and returns a map of validation attributes.
        The timeout option is the server-side timeout value in seconds; floating-point numbers (e.g. 4.9, 3.55) are permitted; max is 30 (seconds).
        If timeout is set, it overrides the default server-side timeout.
        """
        if timeout:
            url = '%s&email=%s&timeout=%.1f' % (self.base_ev_path, quote(email), timeout)
        else:
            url = '%s&email=%s' % (self.base_ev_path, quote(email))
        return self.__do_get_request(url)
    
    def query_by_email(self, email, hash_email=False, fields=None):
        """
        Takes an e-mail and returns a hash which maps attribute fields onto attributes
        If the hash_email option is set, then the email will be hashed before it's sent to TowerData.
        If the fields parameter is set, this comma-separated string specifies the attributes you want returned,
        otherwise, all the attributes configured in your API key are returned.
        You will only be charged for the data you receive.
        """
        if hash_email:
            s = hashlib.sha1()
            s.update(email.lower())
            return self.query_by_sha1(s.hexdigest(), fields)
        if fields:
            url = '%s&email=%s&fields=%s' % (self.base_path, quote(email), quote(fields))
        else:
            url = '%s&email=%s' % (self.base_path, quote(email))
        return self.__do_get_request(url)

    def do_bulk_query(self, data, fields=None):
        """
        Takes a list of e-mails and returns a hash with emails as keys and api information for each email
        The data should be following form: [{'email': '*****@*****.**'}, {'email': '*****@*****.**'}]
        If the fields parameter is set, this comma-separated string specifies the attributes you want returned,
        otherwise, all the attributes configured in your API key are returned.
        You will only be charged for the data you receive.
        For more information refer to http://docs.towerdata.com/#bulk-email-intelligence-introduction
        """
        if fields:
            url = '%s&fields=%s' % (self.base_path,  quote(fields))
        else:
            url = self.base_path
        return self.__do_post_request(data)

    def query_by_md5(self, md5_email, fields=None):
        """
        Takes an e-mail that has already been hashed by md5
        and returns a hash which maps attribute fields onto attributes.
        If the fields parameter is set, this comma-separated string specifies the attributes you want returned,
        otherwise, all the attributes configured in your API key are returned.
        You will only be charged for the data you receive.
        """
        if fields:
            url = '%s&md5_email=%s&fields=%s' % (self.base_path, quote(md5_email), quote(fields))
        else:
            url = '%s&md5_email=%s' % (self.base_path, quote(md5_email))
        return self.__do_get_request(url)
    
    def query_by_sha1(self, sha1_email, fields=None):
        """
        Takes an e-mail that has already been hashed by sha1
        and returns a hash which maps attribute fields onto attributes.
        If the fields parameter is set, this comma-separated string specifies the attributes you want returned,
        otherwise, all the attributes configured in your API key are returned.
        You will only be charged for the data you receive.
        """
        if fields:
            url = '%s&sha1_email=%s&fields=%s' % (self.base_path, quote(sha1_email), quote(fields))
        else:
            url = '%s&sha1_email=%s' % (self.base_path, quote(sha1_email))
        return self.__do_get_request(url)
        
    def query_by_nap(self, first, last, street, city, state, email=None):
        """
        Takes first name, last name, and postal (street, city, and state acronym),
        and returns a hash which maps attribute fields onto attributes
        Though not necessary, adding an e-mail increases hit rate.
        """
        url = '%s&first=%s&last=%s&street=%s&city=%s&state=%s' % (
            self.base_path, quote(first), quote(last), 
            quote(street), quote(city), quote(state))
        if email:
            url = '%s&email=%s' % (url, quote(email))
        return self.__do_get_request(url)
    
    def query_by_naz(self, first, last, zip4, email=None):
        """
        Takes first name, last name, and zip4 code (5-digit zip 
        and 4-digit extension separated by a dash as a string),
        and returns a hash which maps attribute fields onto attributes
        Though not necessary, adding an e-mail increases hit rate.
        """
        url = '%s&first=%s&last=%s&zip4=%s' % (
            self.base_path, quote(first), quote(last), zip4)
        if email:
            url = '%s&email=%s' % (url, quote(email))
        return self.__do_get_request(url)

    def append_email(self, first, last, street, city, state, zip):
        """
        Takes first name, last name, and postal (street, city, state acronym, and zip),
        and returns a hash which maps attribute fields onto attributes
        """
        url = '%s&first=%s&last=%s&street=%s&city=%s&state=%s&zip=%s' % (
            self.base_eppend_path, quote(first), quote(last),
            quote(street), quote(city), quote(state), quote(zip))
        return self.__do_get_request(url)

    def append_postal(self, email):
        """
        Takes email and returns a hash with first name, last name, and postal (street, city, state acronym, and zip) attributes.
        """
        url = '%s&email=%s' % (self.base_eppend_path, quote(email))
        return self.__do_get_request(url)

    def __do_get_request(self, path):
        """
        Pre: Path is an extension to personalize.rapleaf.com
        Note that an exception is raised if an HTTP response code
        other than 200 is sent back. In this case, both the error code
        the error code and error body are accessible from the exception raised
        """
        json_response = self.handle.request('GET', path, headers=TowerDataApi.HEADERS)
        if 200 <= json_response.status < 300:
            if json_response.data:
                return http_response_to_json(json_response.data)
            else:
                return {}
        else:
            raise Exception(json_response.status, json_response.data.decode("utf-8"))

    def __do_post_request(self, data):
        """
        Note that an exception is raised if an HTTP response code
        other than 200 is sent back. In this case, both the error code
        the error code and error body are accessible from the exception raised
        """
        json_response = self.handle.request(
            'POST', self.base_bulk_path, headers=TowerDataApi.POST_HEADERS,
            body=json.JSONEncoder().encode(data)
        )
        if 200 <= json_response.status < 300:
            if json_response.data:
                resp = http_response_to_json(json_response.data)
                ret = {}
                for i, tmp in enumerate(data):
                    ret[tmp['email']] = resp[i]
                return ret
            else:
                return {}
        else:
            raise Exception(
                json_response.status, json_response.data.decode("utf-8")
            )
Beispiel #59
0
class TestHTTPS(HTTPSDummyServerTestCase):
    def setUp(self):
        self._pool = HTTPSConnectionPool(self.host, self.port)
        self.addCleanup(self._pool.close)

    def test_simple(self):
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_dotted_fqdn(self):
        pool = HTTPSConnectionPool(self.host + '.', self.port)
        r = pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_set_ssl_version_to_tlsv1(self):
        self._pool.ssl_version = ssl.PROTOCOL_TLSv1
        r = self._pool.request('GET', '/')
        self.assertEqual(r.status, 200, r.data)

    def test_client_intermediate(self):
        client_cert, client_key, client_subject = (
            DEFAULT_CLIENT_CERTS['certfile'], DEFAULT_CLIENT_CERTS['keyfile'],
            DEFAULT_CLIENT_CERTS['subject'])
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         key_file=client_key,
                                         cert_file=client_cert)
        r = https_pool.request('GET', '/certificate')
        self.assertDictEqual(json.loads(r.data.decode('utf-8')),
                             client_subject, r.data)

    def test_client_no_intermediate(self):
        client_cert, client_key = (
            DEFAULT_CLIENT_NO_INTERMEDIATE_CERTS['certfile'],
            DEFAULT_CLIENT_NO_INTERMEDIATE_CERTS['keyfile'])
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_file=client_cert,
                                         key_file=client_key)
        try:
            https_pool.request('GET', '/certificate', retries=False)
        except SSLError as e:
            self.assertTrue('alert unknown ca' in str(e)
                            or 'invalid certificate chain' in str(e)
                            or 'unknown Cert Authority' in str(e))

    def test_verified(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

        with mock.patch('warnings.warn') as warn:
            r = https_pool.request('GET', '/')
            self.assertEqual(r.status, 200)

            # Modern versions of Python, or systems using PyOpenSSL, don't
            # emit warnings.
            if sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL:
                self.assertFalse(warn.called, warn.call_args_list)
            else:
                self.assertTrue(warn.called)
                if util.HAS_SNI:
                    call = warn.call_args_list[0]
                else:
                    call = warn.call_args_list[1]
                error = call[0][1]
                self.assertEqual(error, InsecurePlatformWarning)

    def test_verified_with_context(self):
        ctx = util.ssl_.create_urllib3_context(cert_reqs=ssl.CERT_REQUIRED)
        ctx.load_verify_locations(cafile=DEFAULT_CA)
        https_pool = HTTPSConnectionPool(self.host, self.port, ssl_context=ctx)
        self.addCleanup(https_pool.close)

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

        with mock.patch('warnings.warn') as warn:
            r = https_pool.request('GET', '/')
            self.assertEqual(r.status, 200)

            # Modern versions of Python, or systems using PyOpenSSL, don't
            # emit warnings.
            if sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL:
                self.assertFalse(warn.called, warn.call_args_list)
            else:
                self.assertTrue(warn.called)
                if util.HAS_SNI:
                    call = warn.call_args_list[0]
                else:
                    call = warn.call_args_list[1]
                error = call[0][1]
                self.assertEqual(error, InsecurePlatformWarning)

    def test_context_combines_with_ca_certs(self):
        ctx = util.ssl_.create_urllib3_context(cert_reqs=ssl.CERT_REQUIRED)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         ca_certs=DEFAULT_CA,
                                         ssl_context=ctx)
        self.addCleanup(https_pool.close)

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

        with mock.patch('warnings.warn') as warn:
            r = https_pool.request('GET', '/')
            self.assertEqual(r.status, 200)

            # Modern versions of Python, or systems using PyOpenSSL, don't
            # emit warnings.
            if sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL:
                self.assertFalse(warn.called, warn.call_args_list)
            else:
                self.assertTrue(warn.called)
                if util.HAS_SNI:
                    call = warn.call_args_list[0]
                else:
                    call = warn.call_args_list[1]
                error = call[0][1]
                self.assertEqual(error, InsecurePlatformWarning)

    @onlyPy279OrNewer
    @notSecureTransport
    def test_ca_dir_verified(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_cert_dir=DEFAULT_CA_DIR)
        self.addCleanup(https_pool.close)

        conn = https_pool._new_conn()
        self.assertEqual(conn.__class__, VerifiedHTTPSConnection)

        with mock.patch('warnings.warn') as warn:
            r = https_pool.request('GET', '/')
            self.assertEqual(r.status, 200)
            self.assertFalse(warn.called, warn.call_args_list)

    def test_invalid_common_name(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL invalid common name")
        except MaxRetryError as e:
            self.assertIsInstance(e.reason, SSLError)
            self.assertTrue("doesn't match" in str(e.reason)
                            or "certificate verify failed" in str(e.reason))

    def test_verified_with_bad_ca_certs(self):
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA_BAD)
        self.addCleanup(https_pool.close)

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL error with bad CA certs")
        except MaxRetryError as e:
            self.assertIsInstance(e.reason, SSLError)
            self.assertTrue(
                'certificate verify failed' in str(e.reason),
                "Expected 'certificate verify failed',"
                "instead got: %r" % e.reason)

    def test_verified_without_ca_certs(self):
        # default is cert_reqs=None which is ssl.CERT_NONE
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED')
        self.addCleanup(https_pool.close)

        try:
            https_pool.request('GET', '/')
            self.fail("Didn't raise SSL error with no CA certs when"
                      "CERT_REQUIRED is set")
        except MaxRetryError as e:
            self.assertIsInstance(e.reason, SSLError)
            # there is a different error message depending on whether or
            # not pyopenssl is injected
            self.assertTrue(
                'No root certificates specified' in str(e.reason)
                or 'certificate verify failed' in str(e.reason)
                or 'invalid certificate chain' in str(e.reason),
                "Expected 'No root certificates specified',  "
                "'certificate verify failed', or "
                "'invalid certificate chain', "
                "instead got: %r" % e.reason)

    def test_no_ssl(self):
        pool = HTTPSConnectionPool(self.host, self.port)
        pool.ConnectionCls = None
        self.addCleanup(pool.close)
        self.assertRaises(SSLError, pool._new_conn)
        with self.assertRaises(MaxRetryError) as cm:
            pool.request('GET', '/', retries=0)
        self.assertIsInstance(cm.exception.reason, SSLError)

    def test_unverified_ssl(self):
        """ Test that bare HTTPSConnection can connect, make requests """
        pool = HTTPSConnectionPool(self.host, self.port)
        pool.ConnectionCls = UnverifiedHTTPSConnection
        self.addCleanup(pool.close)

        with mock.patch('warnings.warn') as warn:
            r = pool.request('GET', '/')
            self.assertEqual(r.status, 200)
            self.assertTrue(warn.called)

            # Modern versions of Python, or systems using PyOpenSSL, only emit
            # the unverified warning. Older systems may also emit other
            # warnings, which we want to ignore here.
            calls = warn.call_args_list
            if sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL:
                category = calls[0][0][1]
            elif util.HAS_SNI:
                category = calls[1][0][1]
            else:
                category = calls[2][0][1]
            self.assertEqual(category, InsecureRequestWarning)

    def test_ssl_unverified_with_ca_certs(self):
        pool = HTTPSConnectionPool(self.host,
                                   self.port,
                                   cert_reqs='CERT_NONE',
                                   ca_certs=DEFAULT_CA_BAD)
        self.addCleanup(pool.close)

        with mock.patch('warnings.warn') as warn:
            r = pool.request('GET', '/')
            self.assertEqual(r.status, 200)
            self.assertTrue(warn.called)

            # Modern versions of Python, or systems using PyOpenSSL, only emit
            # the unverified warning. Older systems may also emit other
            # warnings, which we want to ignore here.
            calls = warn.call_args_list
            if sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL:
                category = calls[0][0][1]
            elif util.HAS_SNI:
                category = calls[1][0][1]
            else:
                category = calls[2][0][1]
            self.assertEqual(category, InsecureRequestWarning)

    def test_assert_hostname_false(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_hostname = False
        https_pool.request('GET', '/')

    def test_assert_specific_hostname(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_hostname = 'localhost'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_md5(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_fingerprint = 'F2:06:5A:42:10:3F:45:1C:17:FE:E6:' \
                                        '07:1E:8A:86:E5'

        https_pool.request('GET', '/')

    def test_assert_fingerprint_sha1(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'
        https_pool.request('GET', '/')

    def test_assert_fingerprint_sha256(self):
        https_pool = HTTPSConnectionPool('localhost',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_fingerprint = ('C5:4D:0B:83:84:89:2E:AE:B4:58:BB:12:'
                                         'F7:A6:C4:76:05:03:88:D8:57:65:51:F3:'
                                         '1E:60:B0:8B:70:18:64:E6')
        https_pool.request('GET', '/')

    def test_assert_invalid_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_fingerprint = 'AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:' \
                                        'AA:AA:AA:AA:AA:AA:AA:AA:AA'

        def _test_request(pool):
            with self.assertRaises(MaxRetryError) as cm:
                pool.request('GET', '/', retries=0)
            self.assertIsInstance(cm.exception.reason, SSLError)

        _test_request(https_pool)
        https_pool._get_conn()

        # Uneven length
        https_pool.assert_fingerprint = 'AA:A'
        _test_request(https_pool)
        https_pool._get_conn()

        # Invalid length
        https_pool.assert_fingerprint = 'AA'
        _test_request(https_pool)

    def test_verify_none_and_bad_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_NONE',
                                         ca_certs=DEFAULT_CA_BAD)
        self.addCleanup(https_pool.close)

        https_pool.assert_fingerprint = 'AA:AA:AA:AA:AA:AAAA:AA:AAAA:AA:' \
                                        'AA:AA:AA:AA:AA:AA:AA:AA:AA'
        with self.assertRaises(MaxRetryError) as cm:
            https_pool.request('GET', '/', retries=0)
        self.assertIsInstance(cm.exception.reason, SSLError)

    def test_verify_none_and_good_fingerprint(self):
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_NONE',
                                         ca_certs=DEFAULT_CA_BAD)
        self.addCleanup(https_pool.close)

        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'
        https_pool.request('GET', '/')

    @notSecureTransport
    def test_good_fingerprint_and_hostname_mismatch(self):
        # This test doesn't run with SecureTransport because we don't turn off
        # hostname validation without turning off all validation, which this
        # test doesn't do (deliberately). We should revisit this if we make
        # new decisions.
        https_pool = HTTPSConnectionPool('127.0.0.1',
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA)
        self.addCleanup(https_pool.close)

        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'
        https_pool.request('GET', '/')

    @requires_network
    def test_https_timeout(self):
        timeout = Timeout(connect=0.001)
        https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        self.addCleanup(https_pool.close)

        timeout = Timeout(total=None, connect=0.001)
        https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        self.addCleanup(https_pool.close)
        self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/')

        timeout = Timeout(read=0.001)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         retries=False,
                                         cert_reqs='CERT_REQUIRED')
        self.addCleanup(https_pool.close)
        https_pool.ca_certs = DEFAULT_CA
        https_pool.assert_fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:' \
                                        'BF:93:CF:F9:71:CC:07:7D:0A'

        timeout = Timeout(total=None)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_NONE')
        self.addCleanup(https_pool.close)
        https_pool.request('GET', '/')

    def test_tunnel(self):
        """ test the _tunnel behavior """
        timeout = Timeout(total=None)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         timeout=timeout,
                                         cert_reqs='CERT_NONE')
        self.addCleanup(https_pool.close)
        conn = https_pool._new_conn()
        self.addCleanup(conn.close)
        try:
            conn.set_tunnel(self.host, self.port)
        except AttributeError:  # python 2.6
            conn._set_tunnel(self.host, self.port)
        conn._tunnel = mock.Mock()
        https_pool._make_request(conn, 'GET', '/')
        conn._tunnel.assert_called_once_with()

    @onlyPy26OrOlder
    def test_tunnel_old_python(self):
        """HTTPSConnection can still make connections if _tunnel_host isn't set

        The _tunnel_host attribute was added in 2.6.3 - because our test runners
        generally use the latest Python 2.6, we simulate the old version by
        deleting the attribute from the HTTPSConnection.
        """
        conn = self._pool._new_conn()
        del conn._tunnel_host
        self._pool._make_request(conn, 'GET', '/')

    @requires_network
    def test_enhanced_timeout(self):
        def new_pool(timeout, cert_reqs='CERT_REQUIRED'):
            https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                             self.port,
                                             timeout=timeout,
                                             retries=False,
                                             cert_reqs=cert_reqs)
            self.addCleanup(https_pool.close)
            return https_pool

        https_pool = new_pool(Timeout(connect=0.001))
        conn = https_pool._new_conn()
        self.assertRaises(ConnectTimeoutError, https_pool.request, 'GET', '/')
        self.assertRaises(ConnectTimeoutError, https_pool._make_request, conn,
                          'GET', '/')

        https_pool = new_pool(Timeout(connect=5))
        self.assertRaises(ConnectTimeoutError,
                          https_pool.request,
                          'GET',
                          '/',
                          timeout=Timeout(connect=0.001))

        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))

    def test_enhanced_ssl_connection(self):
        fingerprint = '92:81:FE:85:F7:0C:26:60:EC:D6:B3:BF:93:CF:F9:71:CC:07:7D:0A'

        conn = VerifiedHTTPSConnection(self.host, self.port)
        self.addCleanup(conn.close)
        https_pool = HTTPSConnectionPool(self.host,
                                         self.port,
                                         cert_reqs='CERT_REQUIRED',
                                         ca_certs=DEFAULT_CA,
                                         assert_fingerprint=fingerprint)
        self.addCleanup(https_pool.close)

        https_pool._make_request(conn, 'GET', '/')

    def test_ssl_correct_system_time(self):
        self._pool.cert_reqs = 'CERT_REQUIRED'
        self._pool.ca_certs = DEFAULT_CA

        w = self._request_without_resource_warnings('GET', '/')
        self.assertEqual([], w)

    def test_ssl_wrong_system_time(self):
        self._pool.cert_reqs = 'CERT_REQUIRED'
        self._pool.ca_certs = DEFAULT_CA
        with mock.patch('urllib3.connection.datetime') as mock_date:
            mock_date.date.today.return_value = datetime.date(1970, 1, 1)

            w = self._request_without_resource_warnings('GET', '/')

            self.assertEqual(len(w), 1)
            warning = w[0]

            self.assertEqual(SystemTimeWarning, warning.category)
            self.assertTrue(str(RECENT_DATE) in warning.message.args[0])

    def _request_without_resource_warnings(self, method, url):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            self._pool.request(method, url)

        return [x for x in w if not isinstance(x.message, ResourceWarning)]
Beispiel #60
0
 def new_pool(timeout, cert_reqs='CERT_REQUIRED'):
     https_pool = HTTPSConnectionPool(TARPIT_HOST,
                                      self.port,
                                      timeout=timeout,
                                      cert_reqs=cert_reqs)
     return https_pool