Issue #17107: Test client-side SNI support in urllib.request thanks to the new server-side SNI support in the ssl module.

Initial patch by Daniel Black.
This commit is contained in:
Antoine Pitrou 2013-02-05 21:20:51 +01:00
parent ca4220be1f
commit da23259f99
6 changed files with 36 additions and 30 deletions

View File

@ -147,9 +147,11 @@ class HTTPSServerThread(threading.Thread):
self.server.shutdown()
def make_https_server(case, certfile=CERTFILE, host=HOST, handler_class=None):
# we assume the certfile contains both private key and certificate
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
def make_https_server(case, *, context=None, certfile=CERTFILE,
host=HOST, handler_class=None):
if context is None:
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
# We assume the certfile contains both private key and certificate
context.load_cert_chain(certfile)
server = HTTPSServerThread(context, host, handler_class)
flag = threading.Event()

View File

@ -703,7 +703,7 @@ class HTTPSTest(TestCase):
def make_server(self, certfile):
from test.ssl_servers import make_https_server
return make_https_server(self, certfile)
return make_https_server(self, certfile=certfile)
def test_attributes(self):
# simple test to check it's storing the timeout

View File

@ -1637,7 +1637,7 @@ else:
def test_socketserver(self):
"""Using a SocketServer to create and manage SSL connections."""
server = make_https_server(self, CERTFILE)
server = make_https_server(self, certfile=CERTFILE)
# try to connect
if support.verbose:
sys.stdout.write('\n')

View File

@ -9,7 +9,10 @@ import unittest
import hashlib
from test import support
threading = support.import_module('threading')
try:
import ssl
except ImportError:
ssl = None
here = os.path.dirname(__file__)
# Self-signed cert file for 'localhost'
@ -17,6 +20,7 @@ CERT_localhost = os.path.join(here, 'keycert.pem')
# Self-signed cert file for 'fakehostname'
CERT_fakehostname = os.path.join(here, 'keycert2.pem')
# Loopback http server infrastructure
class LoopbackHttpServer(http.server.HTTPServer):
@ -353,12 +357,15 @@ class TestUrlopen(unittest.TestCase):
def setUp(self):
super(TestUrlopen, self).setUp()
# Ignore proxies for localhost tests.
self.old_environ = os.environ.copy()
os.environ['NO_PROXY'] = '*'
self.server = None
def tearDown(self):
if self.server is not None:
self.server.stop()
os.environ.clear()
os.environ.update(self.old_environ)
super(TestUrlopen, self).tearDown()
def urlopen(self, url, data=None, **kwargs):
@ -386,14 +393,14 @@ class TestUrlopen(unittest.TestCase):
handler.port = port
return handler
def start_https_server(self, responses=None, certfile=CERT_localhost):
def start_https_server(self, responses=None, **kwargs):
if not hasattr(urllib.request, 'HTTPSHandler'):
self.skipTest('ssl support required')
from test.ssl_servers import make_https_server
if responses is None:
responses = [(200, [], b"we care a bit")]
handler = GetRequestHandler(responses)
server = make_https_server(self, certfile=certfile, handler_class=handler)
server = make_https_server(self, handler_class=handler, **kwargs)
handler.port = server.port
return handler
@ -483,6 +490,21 @@ class TestUrlopen(unittest.TestCase):
self.urlopen("https://localhost:%s/bizarre" % handler.port,
cadefault=True)
def test_https_sni(self):
if ssl is None:
self.skipTest("ssl module required")
if not ssl.HAS_SNI:
self.skipTest("SNI support required in OpenSSL")
sni_name = None
def cb_sni(ssl_sock, server_name, initial_context):
nonlocal sni_name
sni_name = server_name
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.set_servername_callback(cb_sni)
handler = self.start_https_server(context=context, certfile=CERT_localhost)
self.urlopen("https://localhost:%s" % handler.port)
self.assertEqual(sni_name, "localhost")
def test_sending_headers(self):
handler = self.start_server()
req = urllib.request.Request("http://localhost:%s/" % handler.port,

View File

@ -330,31 +330,9 @@ class TimeoutTest(unittest.TestCase):
self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60)
@unittest.skipUnless(ssl, "requires SSL support")
class HTTPSTests(unittest.TestCase):
def test_sni(self):
self.skipTest("test disabled - test server needed")
# Checks that Server Name Indication works, if supported by the
# OpenSSL linked to.
# The ssl module itself doesn't have server-side support for SNI,
# so we rely on a third-party test site.
expect_sni = ssl.HAS_SNI
with support.transient_internet("XXX"):
u = urllib.request.urlopen("XXX")
contents = u.readall()
if expect_sni:
self.assertIn(b"Great", contents)
self.assertNotIn(b"Unfortunately", contents)
else:
self.assertNotIn(b"Great", contents)
self.assertIn(b"Unfortunately", contents)
def test_main():
support.requires("network")
support.run_unittest(AuthTests,
HTTPSTests,
OtherNetworkTests,
CloseSocketTest,
TimeoutTest,

View File

@ -754,6 +754,10 @@ Extension Modules
Tests
-----
- Issue #17107: Test client-side SNI support in urllib.request thanks to
the new server-side SNI support in the ssl module. Initial patch by
Daniel Black.
- Issue #17041: Fix testing when Python is configured with the
--without-doc-strings.