From: Ben Darnell Date: Tue, 5 Nov 2013 20:53:51 +0000 (-0500) Subject: Add backports.ssl_match_hostname dependency in place of our copy. X-Git-Tag: v3.2.0b1~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a5fdb1e83bde31544a854deefc8754b5bc9ae10;p=thirdparty%2Ftornado.git Add backports.ssl_match_hostname dependency in place of our copy. This function has changed recently and it makes more sense to stop maintaining a separate copy, even though it does introduce our first required dependency. --- diff --git a/docs/index.rst b/docs/index.rst index 61aca470e..9a618af1c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,6 +17,13 @@ can scale to tens of thousands of open connections, making it ideal for `WebSockets `_, and other applications that require a long-lived connection to each user. +Upgrade notes +------------- + +As of Tornado 3.2, the `backports.ssl_match_hostname +`_ package +must be installed when running Tornado on Python 2. This will be +installed automatically when using ``pip`` or ``easy_install``. Quick links ----------- @@ -78,9 +85,13 @@ copy of the source tarball as well. The Tornado source code is `hosted on GitHub `_. -**Prerequisites**: Tornado runs on Python 2.6, 2.7, 3.2, and 3.3. It has -no strict dependencies outside the Python standard library, although some -features may require one of the following libraries: +**Prerequisites**: Tornado runs on Python 2.6, 2.7, 3.2, and 3.3. On +Python 2, the `backports.ssl_match_hostname +`_ package +must be installed (This will be installed automatically when using +``pip`` or ``easy_install``); on Python 3 there are no strict +dependencies outside the standard library. Some Tornado features may +require one of the following optional libraries: * `unittest2 `_ is needed to run Tornado's test suite on Python 2.6 (it is unnecessary on more recent diff --git a/maint/requirements.txt b/maint/requirements.txt index be94459bb..18d997319 100644 --- a/maint/requirements.txt +++ b/maint/requirements.txt @@ -1,5 +1,8 @@ # Frozen pip requirements for tools used in the development of tornado +# Tornado's required dependencies +backports.ssl-match-hostname==3.4.0.2 + # Tornado's optional dependencies Twisted==13.0.0 futures==2.1.3 diff --git a/setup.py b/setup.py index 1d019fc7c..5d6032277 100644 --- a/setup.py +++ b/setup.py @@ -14,14 +14,15 @@ # License for the specific language governing permissions and limitations # under the License. -import distutils.core import sys -# Importing setuptools adds some features like "setup.py develop", but -# it's optional so swallow the error if it's not there. + try: + # Use setuptools if available, for install_requires (among other things). import setuptools + from setuptools import setup except ImportError: - pass + setuptools = None + from distutils.core import setup try: from Cython.Build import cythonize @@ -33,18 +34,20 @@ kwargs = {} version = "3.2.dev2" with open('README.rst') as f: - long_description = f.read() + kwargs['long_description'] = f.read() if cythonize is not None: - extensions = cythonize('tornado/speedups.pyx') -else: - extensions = [] + kwargs['ext_modules'] = cythonize('tornado/speedups.pyx') + +if setuptools is not None: + # If setuptools is not available, you're on your own for dependencies. + if sys.version_info < (3, 2): + kwargs['install_requires'] = ['backports.ssl_match_hostname'] -distutils.core.setup( +setuptools.setup( name="tornado", version=version, packages = ["tornado", "tornado.test", "tornado.platform"], - ext_modules = extensions, package_data = { "tornado": ["ca-certificates.crt"], # data files need to be listed both here (which determines what gets @@ -79,6 +82,5 @@ distutils.core.setup( 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], - long_description=long_description, **kwargs ) diff --git a/tornado/netutil.py b/tornado/netutil.py index 9dc8506eb..21db47557 100644 --- a/tornado/netutil.py +++ b/tornado/netutil.py @@ -20,7 +20,6 @@ from __future__ import absolute_import, division, print_function, with_statement import errno import os -import re import socket import ssl import stat @@ -30,6 +29,13 @@ from tornado.ioloop import IOLoop from tornado.platform.auto import set_close_exec from tornado.util import Configurable +if hasattr(ssl, 'match_hostname') and hasattr(ssl, 'CertificateError'): # python 3.2+ + ssl_match_hostname = ssl.match_hostname + SSLCertificateError = ssl.CertificateError +else: + import backports.ssl_match_hostname + ssl_match_hostname = backports.ssl_match_hostname.match_hostname + SSLCertificateError = backports.ssl_match_hostname.CertificateError def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128, flags=None): """Creates listening sockets bound to the given port and address. @@ -391,73 +397,3 @@ def ssl_wrap_socket(socket, ssl_options, server_hostname=None, **kwargs): return context.wrap_socket(socket, **kwargs) else: return ssl.wrap_socket(socket, **dict(context, **kwargs)) - -if hasattr(ssl, 'match_hostname') and hasattr(ssl, 'CertificateError'): # python 3.2+ - ssl_match_hostname = ssl.match_hostname - SSLCertificateError = ssl.CertificateError -else: - # match_hostname was added to the standard library ssl module in python 3.2. - # The following code was backported for older releases and copied from - # https://bitbucket.org/brandon/backports.ssl_match_hostname - class SSLCertificateError(ValueError): - pass - - def _dnsname_to_pat(dn, max_wildcards=1): - pats = [] - for frag in dn.split(r'.'): - if frag.count('*') > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survery of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise SSLCertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) - if frag == '*': - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append('[^.]+') - else: - # Otherwise, '*' matches any dotless fragment. - frag = re.escape(frag) - pats.append(frag.replace(r'\*', '[^.]*')) - return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) - - def ssl_match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules - are mostly followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError("empty or no certificate") - dnsnames = [] - san = cert.get('subjectAltName', ()) - for key, value in san: - if key == 'DNS': - if _dnsname_to_pat(value).match(hostname): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get('subject', ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == 'commonName': - if _dnsname_to_pat(value).match(hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise SSLCertificateError("hostname %r " - "doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) - elif len(dnsnames) == 1: - raise SSLCertificateError("hostname %r " - "doesn't match %r" - % (hostname, dnsnames[0])) - else: - raise SSLCertificateError("no appropriate commonName or " - "subjectAltName fields were found")