mirror of
https://github.com/python/cpython.git
synced 2025-10-09 08:31:26 +00:00
Issue #9003: http.client.HTTPSConnection, urllib.request.HTTPSHandler and
urllib.request.urlopen now take optional arguments to allow for server certificate checking, as recommended in public uses of HTTPS.
This commit is contained in:
parent
bd4dacb3f9
commit
803e6d670c
11 changed files with 418 additions and 160 deletions
|
@ -1,6 +1,7 @@
|
|||
import errno
|
||||
from http import client
|
||||
import io
|
||||
import os
|
||||
import array
|
||||
import socket
|
||||
|
||||
|
@ -9,6 +10,14 @@ TestCase = unittest.TestCase
|
|||
|
||||
from test import support
|
||||
|
||||
here = os.path.dirname(__file__)
|
||||
# Self-signed cert file for 'localhost'
|
||||
CERT_localhost = os.path.join(here, 'keycert.pem')
|
||||
# Self-signed cert file for 'fakehostname'
|
||||
CERT_fakehostname = os.path.join(here, 'keycert2.pem')
|
||||
# Root cert file (CA) for svn.python.org's cert
|
||||
CACERT_svn_python_org = os.path.join(here, 'https_svn_python_org_root.pem')
|
||||
|
||||
HOST = support.HOST
|
||||
|
||||
class FakeSocket:
|
||||
|
@ -370,14 +379,97 @@ class TimeoutTest(TestCase):
|
|||
self.assertEqual(httpConn.sock.gettimeout(), 30)
|
||||
httpConn.close()
|
||||
|
||||
class HTTPSTimeoutTest(TestCase):
|
||||
# XXX Here should be tests for HTTPS, there isn't any right now!
|
||||
|
||||
class HTTPSTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if not hasattr(client, 'HTTPSConnection'):
|
||||
self.skipTest('ssl support required')
|
||||
|
||||
def make_server(self, certfile):
|
||||
from test.ssl_servers import make_https_server
|
||||
return make_https_server(self, certfile)
|
||||
|
||||
def test_attributes(self):
|
||||
# simple test to check it's storing it
|
||||
if hasattr(client, 'HTTPSConnection'):
|
||||
h = client.HTTPSConnection(HOST, TimeoutTest.PORT, timeout=30)
|
||||
self.assertEqual(h.timeout, 30)
|
||||
# simple test to check it's storing the timeout
|
||||
h = client.HTTPSConnection(HOST, TimeoutTest.PORT, timeout=30)
|
||||
self.assertEqual(h.timeout, 30)
|
||||
|
||||
def _check_svn_python_org(self, resp):
|
||||
# Just a simple check that everything went fine
|
||||
server_string = resp.getheader('server')
|
||||
self.assertIn('Apache', server_string)
|
||||
|
||||
def test_networked(self):
|
||||
# Default settings: no cert verification is done
|
||||
support.requires('network')
|
||||
with support.transient_internet('svn.python.org'):
|
||||
h = client.HTTPSConnection('svn.python.org', 443)
|
||||
h.request('GET', '/')
|
||||
resp = h.getresponse()
|
||||
self._check_svn_python_org(resp)
|
||||
|
||||
def test_networked_good_cert(self):
|
||||
# We feed a CA cert that validates the server's cert
|
||||
import ssl
|
||||
support.requires('network')
|
||||
with support.transient_internet('svn.python.org'):
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
context.load_verify_locations(CACERT_svn_python_org)
|
||||
h = client.HTTPSConnection('svn.python.org', 443, context=context)
|
||||
h.request('GET', '/')
|
||||
resp = h.getresponse()
|
||||
self._check_svn_python_org(resp)
|
||||
|
||||
def test_networked_bad_cert(self):
|
||||
# We feed a "CA" cert that is unrelated to the server's cert
|
||||
import ssl
|
||||
support.requires('network')
|
||||
with support.transient_internet('svn.python.org'):
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
context.load_verify_locations(CERT_localhost)
|
||||
h = client.HTTPSConnection('svn.python.org', 443, context=context)
|
||||
with self.assertRaises(ssl.SSLError):
|
||||
h.request('GET', '/')
|
||||
|
||||
def test_local_good_hostname(self):
|
||||
# The (valid) cert validates the HTTP hostname
|
||||
import ssl
|
||||
from test.ssl_servers import make_https_server
|
||||
server = make_https_server(self, CERT_localhost)
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
context.load_verify_locations(CERT_localhost)
|
||||
h = client.HTTPSConnection('localhost', server.port, context=context)
|
||||
h.request('GET', '/nonexistent')
|
||||
resp = h.getresponse()
|
||||
self.assertEqual(resp.status, 404)
|
||||
|
||||
def test_local_bad_hostname(self):
|
||||
# The (valid) cert doesn't validate the HTTP hostname
|
||||
import ssl
|
||||
from test.ssl_servers import make_https_server
|
||||
server = make_https_server(self, CERT_fakehostname)
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
context.load_verify_locations(CERT_fakehostname)
|
||||
h = client.HTTPSConnection('localhost', server.port, context=context)
|
||||
with self.assertRaises(ssl.CertificateError):
|
||||
h.request('GET', '/')
|
||||
# Same with explicit check_hostname=True
|
||||
h = client.HTTPSConnection('localhost', server.port, context=context,
|
||||
check_hostname=True)
|
||||
with self.assertRaises(ssl.CertificateError):
|
||||
h.request('GET', '/')
|
||||
# With check_hostname=False, the mismatching is ignored
|
||||
h = client.HTTPSConnection('localhost', server.port, context=context,
|
||||
check_hostname=False)
|
||||
h.request('GET', '/nonexistent')
|
||||
resp = h.getresponse()
|
||||
self.assertEqual(resp.status, 404)
|
||||
|
||||
|
||||
class RequestBodyTest(TestCase):
|
||||
"""Test cases where a request includes a message body."""
|
||||
|
@ -488,7 +580,7 @@ class HTTPResponseTest(TestCase):
|
|||
|
||||
def test_main(verbose=None):
|
||||
support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
|
||||
HTTPSTimeoutTest, RequestBodyTest, SourceAddressTest,
|
||||
HTTPSTest, RequestBodyTest, SourceAddressTest,
|
||||
HTTPResponseTest)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue