diff options
author | Benjamin Peterson <benjamin@python.org> | 2014-11-24 02:55:24 (GMT) |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2014-11-24 02:55:24 (GMT) |
commit | b206473ef8a7abe9abf5ab8776ea3bcb90adc747 (patch) | |
tree | 94fd8b54f90de94e6e608070871f1c529ba74118 | |
parent | 79828343d8b31b0ccd738490d24c14bafe365099 (diff) | |
download | cpython-b206473ef8a7abe9abf5ab8776ea3bcb90adc747.zip cpython-b206473ef8a7abe9abf5ab8776ea3bcb90adc747.tar.gz cpython-b206473ef8a7abe9abf5ab8776ea3bcb90adc747.tar.bz2 |
give urllib.urlopen a context parameter (closes #22927)
-rw-r--r-- | Doc/library/urllib.rst | 18 | ||||
-rw-r--r-- | Lib/httplib.py | 5 | ||||
-rw-r--r-- | Lib/test/test_urllibnet.py | 20 | ||||
-rw-r--r-- | Lib/urllib.py | 24 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
5 files changed, 54 insertions, 16 deletions
diff --git a/Doc/library/urllib.rst b/Doc/library/urllib.rst index 62f198f..b2a817d 100644 --- a/Doc/library/urllib.rst +++ b/Doc/library/urllib.rst @@ -31,7 +31,7 @@ reading, and no seek operations are available. High-level interface -------------------- -.. function:: urlopen(url[, data[, proxies]]) +.. function:: urlopen(url[, data[, proxies[, context]]]) Open a network object denoted by a URL for reading. If the URL does not have a scheme identifier, or if it has :file:`file:` as its scheme @@ -122,8 +122,12 @@ High-level interface filehandle = urllib.urlopen(some_url, proxies=None) filehandle = urllib.urlopen(some_url) - Proxies which require authentication for use are not currently supported; this - is considered an implementation limitation. + Proxies which require authentication for use are not currently supported; + this is considered an implementation limitation. + + The *context* parameter may be set to a :class:`ssl.SSLContext` instance to + configure the SSL settings that are used if :func:`urlopen` makes a HTTPS + connection. .. versionchanged:: 2.3 Added the *proxies* support. @@ -132,6 +136,9 @@ High-level interface Added :meth:`getcode` to returned object and support for the :envvar:`no_proxy` environment variable. + .. versionchanged:: 2.7.9 + The *context* parameter was added. + .. deprecated:: 2.6 The :func:`urlopen` function has been removed in Python 3 in favor of :func:`urllib2.urlopen`. @@ -292,7 +299,7 @@ Utility functions URL Opener objects ------------------ -.. class:: URLopener([proxies[, **x509]]) +.. class:: URLopener([proxies[, context[, **x509]]]) Base class for opening and reading URLs. Unless you need to support opening objects using schemes other than :file:`http:`, :file:`ftp:`, or :file:`file:`, @@ -309,6 +316,9 @@ URL Opener objects value is ``None``, in which case environmental proxy settings will be used if present, as discussed in the definition of :func:`urlopen`, above. + The *context* parameter may be a :class:`ssl.SSLContext` instance. If given, + it defines the SSL settings the opener uses to make HTTPS connections. + Additional keyword parameters, collected in *x509*, may be used for authentication of the client when using the :file:`https:` scheme. The keywords *key_file* and *cert_file* are supported to provide an SSL key and certificate; diff --git a/Lib/httplib.py b/Lib/httplib.py index 6d2e38d..48fbcb6 100644 --- a/Lib/httplib.py +++ b/Lib/httplib.py @@ -1238,14 +1238,15 @@ else: _connection_class = HTTPSConnection def __init__(self, host='', port=None, key_file=None, cert_file=None, - strict=None): + strict=None, context=None): # provide a default host, pass the X509 cert info # urf. compensate for bad input. if port == 0: port = None self._setup(self._connection_class(host, port, key_file, - cert_file, strict)) + cert_file, strict, + context=context)) # we never actually use these for anything, but we keep them # here for compatibility with post-1.5.2 CVS. diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index 9f24b7ad..7c4b1f7 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -7,6 +7,15 @@ import sys import os import time +try: + import ssl +except ImportError: + ssl = None + +here = os.path.dirname(__file__) +# Self-signed cert file for self-signed.pythontest.net +CERT_selfsigned_pythontestdotnet = os.path.join(here, 'selfsigned_pythontestdotnet.pem') + mimetools = test_support.import_module("mimetools", deprecated=True) @@ -195,6 +204,14 @@ class urlretrieveNetworkTests(unittest.TestCase): self.fail('Date value not in %r format', dateformat) +@unittest.skipIf(ssl is None, "requires ssl") +class urlopen_HttpsTests(unittest.TestCase): + + def test_context_argument(self): + context = ssl.create_default_context(cafile=CERT_selfsigned_pythontestdotnet) + response = urllib.urlopen("https://self-signed.pythontest.net", context=context) + self.assertIn("Python", response.read()) + def test_main(): test_support.requires('network') @@ -202,7 +219,8 @@ def test_main(): ("urllib.urlopen.. has been removed", DeprecationWarning)): test_support.run_unittest(URLTimeoutTest, urlopenNetworkTests, - urlretrieveNetworkTests) + urlretrieveNetworkTests, + urlopen_HttpsTests) if __name__ == "__main__": test_main() diff --git a/Lib/urllib.py b/Lib/urllib.py index ac5d797..2408cb8 100644 --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -69,15 +69,15 @@ else: # Shortcut for basic usage _urlopener = None -def urlopen(url, data=None, proxies=None): +def urlopen(url, data=None, proxies=None, context=None): """Create a file-like object for the specified URL to read from.""" from warnings import warnpy3k warnpy3k("urllib.urlopen() has been removed in Python 3.0 in " "favor of urllib2.urlopen()", stacklevel=2) global _urlopener - if proxies is not None: - opener = FancyURLopener(proxies=proxies) + if proxies is not None or context is not None: + opener = FancyURLopener(proxies=proxies, context=context) elif not _urlopener: opener = FancyURLopener() _urlopener = opener @@ -87,11 +87,15 @@ def urlopen(url, data=None, proxies=None): return opener.open(url) else: return opener.open(url, data) -def urlretrieve(url, filename=None, reporthook=None, data=None): +def urlretrieve(url, filename=None, reporthook=None, data=None, context=None): global _urlopener - if not _urlopener: - _urlopener = FancyURLopener() - return _urlopener.retrieve(url, filename, reporthook, data) + if context is not None: + opener = FancyURLopener(context=context) + elif not _urlopener: + _urlopener = opener = FancyURLopener() + else: + opener = _urlopener + return opener.retrieve(url, filename, reporthook, data) def urlcleanup(): if _urlopener: _urlopener.cleanup() @@ -126,13 +130,14 @@ class URLopener: version = "Python-urllib/%s" % __version__ # Constructor - def __init__(self, proxies=None, **x509): + def __init__(self, proxies=None, context=None, **x509): if proxies is None: proxies = getproxies() assert hasattr(proxies, 'has_key'), "proxies must be a mapping" self.proxies = proxies self.key_file = x509.get('key_file') self.cert_file = x509.get('cert_file') + self.context = context self.addheaders = [('User-Agent', self.version)] self.__tempfiles = [] self.__unlink = os.unlink # See cleanup() @@ -422,7 +427,8 @@ class URLopener: auth = None h = httplib.HTTPS(host, 0, key_file=self.key_file, - cert_file=self.cert_file) + cert_file=self.cert_file, + context=self.context) if data is not None: h.putrequest('POST', selector) h.putheader('Content-Type', @@ -42,6 +42,9 @@ Core and Builtins Library ------- +- Issue #22927: Allow urllib.urlopen to take a *context* parameter to control + SSL settings for HTTPS connections. + - Issue #22921: Allow SSLContext to take the *hostname* parameter even if OpenSSL doesn't support SNI. |