Commit b206473e authored by Benjamin Peterson's avatar Benjamin Peterson

give urllib.urlopen a context parameter (closes #22927)

parent 79828343
......@@ -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;
......
......@@ -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.
......
......@@ -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()
......@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment