We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Uncaught exception from pyOpenSSL's shutdown call
See https://github.com/pypa/pip/issues/2753 for more details


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `urllib3/contrib/pyopenssl.py`
Content:
```
1 '''SSL with SNI_-support for Python 2. Follow these instructions if you would
2 like to verify SSL certificates in Python 2. Note, the default libraries do
3 *not* do certificate checking; you need to do additional work to validate
4 certificates yourself.
5 
6 This needs the following packages installed:
7 
8 * pyOpenSSL (tested with 0.13)
9 * ndg-httpsclient (tested with 0.3.2)
10 * pyasn1 (tested with 0.1.6)
11 
12 You can install them with the following command:
13 
14     pip install pyopenssl ndg-httpsclient pyasn1
15 
16 To activate certificate checking, call
17 :func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code
18 before you begin making HTTP requests. This can be done in a ``sitecustomize``
19 module, or at any other time before your application begins using ``urllib3``,
20 like this::
21 
22     try:
23         import urllib3.contrib.pyopenssl
24         urllib3.contrib.pyopenssl.inject_into_urllib3()
25     except ImportError:
26         pass
27 
28 Now you can use :mod:`urllib3` as you normally would, and it will support SNI
29 when the required modules are installed.
30 
31 Activating this module also has the positive side effect of disabling SSL/TLS
32 compression in Python 2 (see `CRIME attack`_).
33 
34 If you want to configure the default list of supported cipher suites, you can
35 set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable.
36 
37 Module Variables
38 ----------------
39 
40 :var DEFAULT_SSL_CIPHER_LIST: The list of supported SSL/TLS cipher suites.
41 
42 .. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication
43 .. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit)
44 
45 '''
46 
47 try:
48     from ndg.httpsclient.ssl_peer_verification import SUBJ_ALT_NAME_SUPPORT
49     from ndg.httpsclient.subj_alt_name import SubjectAltName as BaseSubjectAltName
50 except SyntaxError as e:
51     raise ImportError(e)
52 
53 import OpenSSL.SSL
54 from pyasn1.codec.der import decoder as der_decoder
55 from pyasn1.type import univ, constraint
56 from socket import _fileobject, timeout
57 import ssl
58 import select
59 
60 from .. import connection
61 from .. import util
62 
63 __all__ = ['inject_into_urllib3', 'extract_from_urllib3']
64 
65 # SNI only *really* works if we can read the subjectAltName of certificates.
66 HAS_SNI = SUBJ_ALT_NAME_SUPPORT
67 
68 # Map from urllib3 to PyOpenSSL compatible parameter-values.
69 _openssl_versions = {
70     ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD,
71     ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
72 }
73 
74 try:
75     _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD})
76 except AttributeError:
77     pass
78 
79 _openssl_verify = {
80     ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE,
81     ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER,
82     ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER
83                        + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
84 }
85 
86 DEFAULT_SSL_CIPHER_LIST = util.ssl_.DEFAULT_CIPHERS
87 
88 
89 orig_util_HAS_SNI = util.HAS_SNI
90 orig_connection_ssl_wrap_socket = connection.ssl_wrap_socket
91 
92 
93 def inject_into_urllib3():
94     'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.'
95 
96     connection.ssl_wrap_socket = ssl_wrap_socket
97     util.HAS_SNI = HAS_SNI
98 
99 
100 def extract_from_urllib3():
101     'Undo monkey-patching by :func:`inject_into_urllib3`.'
102 
103     connection.ssl_wrap_socket = orig_connection_ssl_wrap_socket
104     util.HAS_SNI = orig_util_HAS_SNI
105 
106 
107 ### Note: This is a slightly bug-fixed version of same from ndg-httpsclient.
108 class SubjectAltName(BaseSubjectAltName):
109     '''ASN.1 implementation for subjectAltNames support'''
110 
111     # There is no limit to how many SAN certificates a certificate may have,
112     #   however this needs to have some limit so we'll set an arbitrarily high
113     #   limit.
114     sizeSpec = univ.SequenceOf.sizeSpec + \
115         constraint.ValueSizeConstraint(1, 1024)
116 
117 
118 ### Note: This is a slightly bug-fixed version of same from ndg-httpsclient.
119 def get_subj_alt_name(peer_cert):
120     # Search through extensions
121     dns_name = []
122     if not SUBJ_ALT_NAME_SUPPORT:
123         return dns_name
124 
125     general_names = SubjectAltName()
126     for i in range(peer_cert.get_extension_count()):
127         ext = peer_cert.get_extension(i)
128         ext_name = ext.get_short_name()
129         if ext_name != 'subjectAltName':
130             continue
131 
132         # PyOpenSSL returns extension data in ASN.1 encoded form
133         ext_dat = ext.get_data()
134         decoded_dat = der_decoder.decode(ext_dat,
135                                          asn1Spec=general_names)
136 
137         for name in decoded_dat:
138             if not isinstance(name, SubjectAltName):
139                 continue
140             for entry in range(len(name)):
141                 component = name.getComponentByPosition(entry)
142                 if component.getName() != 'dNSName':
143                     continue
144                 dns_name.append(str(component.getComponent()))
145 
146     return dns_name
147 
148 
149 class WrappedSocket(object):
150     '''API-compatibility wrapper for Python OpenSSL's Connection-class.
151 
152     Note: _makefile_refs, _drop() and _reuse() are needed for the garbage
153     collector of pypy.
154     '''
155 
156     def __init__(self, connection, socket, suppress_ragged_eofs=True):
157         self.connection = connection
158         self.socket = socket
159         self.suppress_ragged_eofs = suppress_ragged_eofs
160         self._makefile_refs = 0
161 
162     def fileno(self):
163         return self.socket.fileno()
164 
165     def makefile(self, mode, bufsize=-1):
166         self._makefile_refs += 1
167         return _fileobject(self, mode, bufsize, close=True)
168 
169     def recv(self, *args, **kwargs):
170         try:
171             data = self.connection.recv(*args, **kwargs)
172         except OpenSSL.SSL.SysCallError as e:
173             if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'):
174                 return b''
175             else:
176                 raise
177         except OpenSSL.SSL.ZeroReturnError as e:
178             if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN:
179                 return b''
180             else:
181                 raise
182         except OpenSSL.SSL.WantReadError:
183             rd, wd, ed = select.select(
184                 [self.socket], [], [], self.socket.gettimeout())
185             if not rd:
186                 raise timeout('The read operation timed out')
187             else:
188                 return self.recv(*args, **kwargs)
189         else:
190             return data
191 
192     def settimeout(self, timeout):
193         return self.socket.settimeout(timeout)
194 
195     def _send_until_done(self, data):
196         while True:
197             try:
198                 return self.connection.send(data)
199             except OpenSSL.SSL.WantWriteError:
200                 _, wlist, _ = select.select([], [self.socket], [],
201                                             self.socket.gettimeout())
202                 if not wlist:
203                     raise timeout()
204                 continue
205 
206     def sendall(self, data):
207         while len(data):
208             sent = self._send_until_done(data)
209             data = data[sent:]
210 
211     def close(self):
212         if self._makefile_refs < 1:
213             return self.connection.shutdown()
214         else:
215             self._makefile_refs -= 1
216 
217     def getpeercert(self, binary_form=False):
218         x509 = self.connection.get_peer_certificate()
219 
220         if not x509:
221             return x509
222 
223         if binary_form:
224             return OpenSSL.crypto.dump_certificate(
225                 OpenSSL.crypto.FILETYPE_ASN1,
226                 x509)
227 
228         return {
229             'subject': (
230                 (('commonName', x509.get_subject().CN),),
231             ),
232             'subjectAltName': [
233                 ('DNS', value)
234                 for value in get_subj_alt_name(x509)
235             ]
236         }
237 
238     def _reuse(self):
239         self._makefile_refs += 1
240 
241     def _drop(self):
242         if self._makefile_refs < 1:
243             self.close()
244         else:
245             self._makefile_refs -= 1
246 
247 
248 def _verify_callback(cnx, x509, err_no, err_depth, return_code):
249     return err_no == 0
250 
251 
252 def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
253                     ca_certs=None, server_hostname=None,
254                     ssl_version=None):
255     ctx = OpenSSL.SSL.Context(_openssl_versions[ssl_version])
256     if certfile:
257         keyfile = keyfile or certfile  # Match behaviour of the normal python ssl library
258         ctx.use_certificate_file(certfile)
259     if keyfile:
260         ctx.use_privatekey_file(keyfile)
261     if cert_reqs != ssl.CERT_NONE:
262         ctx.set_verify(_openssl_verify[cert_reqs], _verify_callback)
263     if ca_certs:
264         try:
265             ctx.load_verify_locations(ca_certs, None)
266         except OpenSSL.SSL.Error as e:
267             raise ssl.SSLError('bad ca_certs: %r' % ca_certs, e)
268     else:
269         ctx.set_default_verify_paths()
270 
271     # Disable TLS compression to migitate CRIME attack (issue #309)
272     OP_NO_COMPRESSION = 0x20000
273     ctx.set_options(OP_NO_COMPRESSION)
274 
275     # Set list of supported ciphersuites.
276     ctx.set_cipher_list(DEFAULT_SSL_CIPHER_LIST)
277 
278     cnx = OpenSSL.SSL.Connection(ctx, sock)
279     cnx.set_tlsext_host_name(server_hostname)
280     cnx.set_connect_state()
281     while True:
282         try:
283             cnx.do_handshake()
284         except OpenSSL.SSL.WantReadError:
285             rd, _, _ = select.select([sock], [], [], sock.gettimeout())
286             if not rd:
287                 raise timeout('select timed out')
288             continue
289         except OpenSSL.SSL.Error as e:
290             raise ssl.SSLError('bad handshake', e)
291         break
292 
293     return WrappedSocket(cnx, sock)
294
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/urllib3/contrib/pyopenssl.py b/urllib3/contrib/pyopenssl.py
--- a/urllib3/contrib/pyopenssl.py
+++ b/urllib3/contrib/pyopenssl.py
@@ -208,9 +208,13 @@
             sent = self._send_until_done(data)
             data = data[sent:]
 
+    def shutdown(self):
+        # FIXME rethrow compatible exceptions should we ever use this
+        self.connection.shutdown()
+
     def close(self):
         if self._makefile_refs < 1:
-            return self.connection.shutdown()
+            return self.connection.close()
         else:
             self._makefile_refs -= 1

{"golden_diff": "diff --git a/urllib3/contrib/pyopenssl.py b/urllib3/contrib/pyopenssl.py\n--- a/urllib3/contrib/pyopenssl.py\n+++ b/urllib3/contrib/pyopenssl.py\n@@ -208,9 +208,13 @@\n             sent = self._send_until_done(data)\n             data = data[sent:]\n \n+    def shutdown(self):\n+        # FIXME rethrow compatible exceptions should we ever use this\n+        self.connection.shutdown()\n+\n     def close(self):\n         if self._makefile_refs < 1:\n-            return self.connection.shutdown()\n+            return self.connection.close()\n         else:\n             self._makefile_refs -= 1\n", "issue": "Uncaught exception from pyOpenSSL's shutdown call\nSee https://github.com/pypa/pip/issues/2753 for more details\n\n", "before_files": [{"content": "'''SSL with SNI_-support for Python 2. Follow these instructions if you would\nlike to verify SSL certificates in Python 2. Note, the default libraries do\n*not* do certificate checking; you need to do additional work to validate\ncertificates yourself.\n\nThis needs the following packages installed:\n\n* pyOpenSSL (tested with 0.13)\n* ndg-httpsclient (tested with 0.3.2)\n* pyasn1 (tested with 0.1.6)\n\nYou can install them with the following command:\n\n    pip install pyopenssl ndg-httpsclient pyasn1\n\nTo activate certificate checking, call\n:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code\nbefore you begin making HTTP requests. This can be done in a ``sitecustomize``\nmodule, or at any other time before your application begins using ``urllib3``,\nlike this::\n\n    try:\n        import urllib3.contrib.pyopenssl\n        urllib3.contrib.pyopenssl.inject_into_urllib3()\n    except ImportError:\n        pass\n\nNow you can use :mod:`urllib3` as you normally would, and it will support SNI\nwhen the required modules are installed.\n\nActivating this module also has the positive side effect of disabling SSL/TLS\ncompression in Python 2 (see `CRIME attack`_).\n\nIf you want to configure the default list of supported cipher suites, you can\nset the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable.\n\nModule Variables\n----------------\n\n:var DEFAULT_SSL_CIPHER_LIST: The list of supported SSL/TLS cipher suites.\n\n.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication\n.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit)\n\n'''\n\ntry:\n    from ndg.httpsclient.ssl_peer_verification import SUBJ_ALT_NAME_SUPPORT\n    from ndg.httpsclient.subj_alt_name import SubjectAltName as BaseSubjectAltName\nexcept SyntaxError as e:\n    raise ImportError(e)\n\nimport OpenSSL.SSL\nfrom pyasn1.codec.der import decoder as der_decoder\nfrom pyasn1.type import univ, constraint\nfrom socket import _fileobject, timeout\nimport ssl\nimport select\n\nfrom .. import connection\nfrom .. import util\n\n__all__ = ['inject_into_urllib3', 'extract_from_urllib3']\n\n# SNI only *really* works if we can read the subjectAltName of certificates.\nHAS_SNI = SUBJ_ALT_NAME_SUPPORT\n\n# Map from urllib3 to PyOpenSSL compatible parameter-values.\n_openssl_versions = {\n    ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD,\n    ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,\n}\n\ntry:\n    _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD})\nexcept AttributeError:\n    pass\n\n_openssl_verify = {\n    ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE,\n    ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER,\n    ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER\n                       + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,\n}\n\nDEFAULT_SSL_CIPHER_LIST = util.ssl_.DEFAULT_CIPHERS\n\n\norig_util_HAS_SNI = util.HAS_SNI\norig_connection_ssl_wrap_socket = connection.ssl_wrap_socket\n\n\ndef inject_into_urllib3():\n    'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.'\n\n    connection.ssl_wrap_socket = ssl_wrap_socket\n    util.HAS_SNI = HAS_SNI\n\n\ndef extract_from_urllib3():\n    'Undo monkey-patching by :func:`inject_into_urllib3`.'\n\n    connection.ssl_wrap_socket = orig_connection_ssl_wrap_socket\n    util.HAS_SNI = orig_util_HAS_SNI\n\n\n### Note: This is a slightly bug-fixed version of same from ndg-httpsclient.\nclass SubjectAltName(BaseSubjectAltName):\n    '''ASN.1 implementation for subjectAltNames support'''\n\n    # There is no limit to how many SAN certificates a certificate may have,\n    #   however this needs to have some limit so we'll set an arbitrarily high\n    #   limit.\n    sizeSpec = univ.SequenceOf.sizeSpec + \\\n        constraint.ValueSizeConstraint(1, 1024)\n\n\n### Note: This is a slightly bug-fixed version of same from ndg-httpsclient.\ndef get_subj_alt_name(peer_cert):\n    # Search through extensions\n    dns_name = []\n    if not SUBJ_ALT_NAME_SUPPORT:\n        return dns_name\n\n    general_names = SubjectAltName()\n    for i in range(peer_cert.get_extension_count()):\n        ext = peer_cert.get_extension(i)\n        ext_name = ext.get_short_name()\n        if ext_name != 'subjectAltName':\n            continue\n\n        # PyOpenSSL returns extension data in ASN.1 encoded form\n        ext_dat = ext.get_data()\n        decoded_dat = der_decoder.decode(ext_dat,\n                                         asn1Spec=general_names)\n\n        for name in decoded_dat:\n            if not isinstance(name, SubjectAltName):\n                continue\n            for entry in range(len(name)):\n                component = name.getComponentByPosition(entry)\n                if component.getName() != 'dNSName':\n                    continue\n                dns_name.append(str(component.getComponent()))\n\n    return dns_name\n\n\nclass WrappedSocket(object):\n    '''API-compatibility wrapper for Python OpenSSL's Connection-class.\n\n    Note: _makefile_refs, _drop() and _reuse() are needed for the garbage\n    collector of pypy.\n    '''\n\n    def __init__(self, connection, socket, suppress_ragged_eofs=True):\n        self.connection = connection\n        self.socket = socket\n        self.suppress_ragged_eofs = suppress_ragged_eofs\n        self._makefile_refs = 0\n\n    def fileno(self):\n        return self.socket.fileno()\n\n    def makefile(self, mode, bufsize=-1):\n        self._makefile_refs += 1\n        return _fileobject(self, mode, bufsize, close=True)\n\n    def recv(self, *args, **kwargs):\n        try:\n            data = self.connection.recv(*args, **kwargs)\n        except OpenSSL.SSL.SysCallError as e:\n            if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'):\n                return b''\n            else:\n                raise\n        except OpenSSL.SSL.ZeroReturnError as e:\n            if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN:\n                return b''\n            else:\n                raise\n        except OpenSSL.SSL.WantReadError:\n            rd, wd, ed = select.select(\n                [self.socket], [], [], self.socket.gettimeout())\n            if not rd:\n                raise timeout('The read operation timed out')\n            else:\n                return self.recv(*args, **kwargs)\n        else:\n            return data\n\n    def settimeout(self, timeout):\n        return self.socket.settimeout(timeout)\n\n    def _send_until_done(self, data):\n        while True:\n            try:\n                return self.connection.send(data)\n            except OpenSSL.SSL.WantWriteError:\n                _, wlist, _ = select.select([], [self.socket], [],\n                                            self.socket.gettimeout())\n                if not wlist:\n                    raise timeout()\n                continue\n\n    def sendall(self, data):\n        while len(data):\n            sent = self._send_until_done(data)\n            data = data[sent:]\n\n    def close(self):\n        if self._makefile_refs < 1:\n            return self.connection.shutdown()\n        else:\n            self._makefile_refs -= 1\n\n    def getpeercert(self, binary_form=False):\n        x509 = self.connection.get_peer_certificate()\n\n        if not x509:\n            return x509\n\n        if binary_form:\n            return OpenSSL.crypto.dump_certificate(\n                OpenSSL.crypto.FILETYPE_ASN1,\n                x509)\n\n        return {\n            'subject': (\n                (('commonName', x509.get_subject().CN),),\n            ),\n            'subjectAltName': [\n                ('DNS', value)\n                for value in get_subj_alt_name(x509)\n            ]\n        }\n\n    def _reuse(self):\n        self._makefile_refs += 1\n\n    def _drop(self):\n        if self._makefile_refs < 1:\n            self.close()\n        else:\n            self._makefile_refs -= 1\n\n\ndef _verify_callback(cnx, x509, err_no, err_depth, return_code):\n    return err_no == 0\n\n\ndef ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,\n                    ca_certs=None, server_hostname=None,\n                    ssl_version=None):\n    ctx = OpenSSL.SSL.Context(_openssl_versions[ssl_version])\n    if certfile:\n        keyfile = keyfile or certfile  # Match behaviour of the normal python ssl library\n        ctx.use_certificate_file(certfile)\n    if keyfile:\n        ctx.use_privatekey_file(keyfile)\n    if cert_reqs != ssl.CERT_NONE:\n        ctx.set_verify(_openssl_verify[cert_reqs], _verify_callback)\n    if ca_certs:\n        try:\n            ctx.load_verify_locations(ca_certs, None)\n        except OpenSSL.SSL.Error as e:\n            raise ssl.SSLError('bad ca_certs: %r' % ca_certs, e)\n    else:\n        ctx.set_default_verify_paths()\n\n    # Disable TLS compression to migitate CRIME attack (issue #309)\n    OP_NO_COMPRESSION = 0x20000\n    ctx.set_options(OP_NO_COMPRESSION)\n\n    # Set list of supported ciphersuites.\n    ctx.set_cipher_list(DEFAULT_SSL_CIPHER_LIST)\n\n    cnx = OpenSSL.SSL.Connection(ctx, sock)\n    cnx.set_tlsext_host_name(server_hostname)\n    cnx.set_connect_state()\n    while True:\n        try:\n            cnx.do_handshake()\n        except OpenSSL.SSL.WantReadError:\n            rd, _, _ = select.select([sock], [], [], sock.gettimeout())\n            if not rd:\n                raise timeout('select timed out')\n            continue\n        except OpenSSL.SSL.Error as e:\n            raise ssl.SSLError('bad handshake', e)\n        break\n\n    return WrappedSocket(cnx, sock)\n", "path": "urllib3/contrib/pyopenssl.py"}], "after_files": [{"content": "'''SSL with SNI_-support for Python 2. Follow these instructions if you would\nlike to verify SSL certificates in Python 2. Note, the default libraries do\n*not* do certificate checking; you need to do additional work to validate\ncertificates yourself.\n\nThis needs the following packages installed:\n\n* pyOpenSSL (tested with 0.13)\n* ndg-httpsclient (tested with 0.3.2)\n* pyasn1 (tested with 0.1.6)\n\nYou can install them with the following command:\n\n    pip install pyopenssl ndg-httpsclient pyasn1\n\nTo activate certificate checking, call\n:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code\nbefore you begin making HTTP requests. This can be done in a ``sitecustomize``\nmodule, or at any other time before your application begins using ``urllib3``,\nlike this::\n\n    try:\n        import urllib3.contrib.pyopenssl\n        urllib3.contrib.pyopenssl.inject_into_urllib3()\n    except ImportError:\n        pass\n\nNow you can use :mod:`urllib3` as you normally would, and it will support SNI\nwhen the required modules are installed.\n\nActivating this module also has the positive side effect of disabling SSL/TLS\ncompression in Python 2 (see `CRIME attack`_).\n\nIf you want to configure the default list of supported cipher suites, you can\nset the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable.\n\nModule Variables\n----------------\n\n:var DEFAULT_SSL_CIPHER_LIST: The list of supported SSL/TLS cipher suites.\n\n.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication\n.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit)\n\n'''\n\ntry:\n    from ndg.httpsclient.ssl_peer_verification import SUBJ_ALT_NAME_SUPPORT\n    from ndg.httpsclient.subj_alt_name import SubjectAltName as BaseSubjectAltName\nexcept SyntaxError as e:\n    raise ImportError(e)\n\nimport OpenSSL.SSL\nfrom pyasn1.codec.der import decoder as der_decoder\nfrom pyasn1.type import univ, constraint\nfrom socket import _fileobject, timeout\nimport ssl\nimport select\n\nfrom .. import connection\nfrom .. import util\n\n__all__ = ['inject_into_urllib3', 'extract_from_urllib3']\n\n# SNI only *really* works if we can read the subjectAltName of certificates.\nHAS_SNI = SUBJ_ALT_NAME_SUPPORT\n\n# Map from urllib3 to PyOpenSSL compatible parameter-values.\n_openssl_versions = {\n    ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD,\n    ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,\n}\n\ntry:\n    _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD})\nexcept AttributeError:\n    pass\n\n_openssl_verify = {\n    ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE,\n    ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER,\n    ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER\n                       + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,\n}\n\nDEFAULT_SSL_CIPHER_LIST = util.ssl_.DEFAULT_CIPHERS\n\n\norig_util_HAS_SNI = util.HAS_SNI\norig_connection_ssl_wrap_socket = connection.ssl_wrap_socket\n\n\ndef inject_into_urllib3():\n    'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.'\n\n    connection.ssl_wrap_socket = ssl_wrap_socket\n    util.HAS_SNI = HAS_SNI\n\n\ndef extract_from_urllib3():\n    'Undo monkey-patching by :func:`inject_into_urllib3`.'\n\n    connection.ssl_wrap_socket = orig_connection_ssl_wrap_socket\n    util.HAS_SNI = orig_util_HAS_SNI\n\n\n### Note: This is a slightly bug-fixed version of same from ndg-httpsclient.\nclass SubjectAltName(BaseSubjectAltName):\n    '''ASN.1 implementation for subjectAltNames support'''\n\n    # There is no limit to how many SAN certificates a certificate may have,\n    #   however this needs to have some limit so we'll set an arbitrarily high\n    #   limit.\n    sizeSpec = univ.SequenceOf.sizeSpec + \\\n        constraint.ValueSizeConstraint(1, 1024)\n\n\n### Note: This is a slightly bug-fixed version of same from ndg-httpsclient.\ndef get_subj_alt_name(peer_cert):\n    # Search through extensions\n    dns_name = []\n    if not SUBJ_ALT_NAME_SUPPORT:\n        return dns_name\n\n    general_names = SubjectAltName()\n    for i in range(peer_cert.get_extension_count()):\n        ext = peer_cert.get_extension(i)\n        ext_name = ext.get_short_name()\n        if ext_name != 'subjectAltName':\n            continue\n\n        # PyOpenSSL returns extension data in ASN.1 encoded form\n        ext_dat = ext.get_data()\n        decoded_dat = der_decoder.decode(ext_dat,\n                                         asn1Spec=general_names)\n\n        for name in decoded_dat:\n            if not isinstance(name, SubjectAltName):\n                continue\n            for entry in range(len(name)):\n                component = name.getComponentByPosition(entry)\n                if component.getName() != 'dNSName':\n                    continue\n                dns_name.append(str(component.getComponent()))\n\n    return dns_name\n\n\nclass WrappedSocket(object):\n    '''API-compatibility wrapper for Python OpenSSL's Connection-class.\n\n    Note: _makefile_refs, _drop() and _reuse() are needed for the garbage\n    collector of pypy.\n    '''\n\n    def __init__(self, connection, socket, suppress_ragged_eofs=True):\n        self.connection = connection\n        self.socket = socket\n        self.suppress_ragged_eofs = suppress_ragged_eofs\n        self._makefile_refs = 0\n\n    def fileno(self):\n        return self.socket.fileno()\n\n    def makefile(self, mode, bufsize=-1):\n        self._makefile_refs += 1\n        return _fileobject(self, mode, bufsize, close=True)\n\n    def recv(self, *args, **kwargs):\n        try:\n            data = self.connection.recv(*args, **kwargs)\n        except OpenSSL.SSL.SysCallError as e:\n            if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'):\n                return b''\n            else:\n                raise\n        except OpenSSL.SSL.ZeroReturnError as e:\n            if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN:\n                return b''\n            else:\n                raise\n        except OpenSSL.SSL.WantReadError:\n            rd, wd, ed = select.select(\n                [self.socket], [], [], self.socket.gettimeout())\n            if not rd:\n                raise timeout('The read operation timed out')\n            else:\n                return self.recv(*args, **kwargs)\n        else:\n            return data\n\n    def settimeout(self, timeout):\n        return self.socket.settimeout(timeout)\n\n    def _send_until_done(self, data):\n        while True:\n            try:\n                return self.connection.send(data)\n            except OpenSSL.SSL.WantWriteError:\n                _, wlist, _ = select.select([], [self.socket], [],\n                                            self.socket.gettimeout())\n                if not wlist:\n                    raise timeout()\n                continue\n\n    def sendall(self, data):\n        while len(data):\n            sent = self._send_until_done(data)\n            data = data[sent:]\n\n    def shutdown(self):\n        # FIXME rethrow compatible exceptions should we ever use this\n        self.connection.shutdown()\n\n    def close(self):\n        if self._makefile_refs < 1:\n            return self.connection.close()\n        else:\n            self._makefile_refs -= 1\n\n    def getpeercert(self, binary_form=False):\n        x509 = self.connection.get_peer_certificate()\n\n        if not x509:\n            return x509\n\n        if binary_form:\n            return OpenSSL.crypto.dump_certificate(\n                OpenSSL.crypto.FILETYPE_ASN1,\n                x509)\n\n        return {\n            'subject': (\n                (('commonName', x509.get_subject().CN),),\n            ),\n            'subjectAltName': [\n                ('DNS', value)\n                for value in get_subj_alt_name(x509)\n            ]\n        }\n\n    def _reuse(self):\n        self._makefile_refs += 1\n\n    def _drop(self):\n        if self._makefile_refs < 1:\n            self.close()\n        else:\n            self._makefile_refs -= 1\n\n\ndef _verify_callback(cnx, x509, err_no, err_depth, return_code):\n    return err_no == 0\n\n\ndef ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,\n                    ca_certs=None, server_hostname=None,\n                    ssl_version=None):\n    ctx = OpenSSL.SSL.Context(_openssl_versions[ssl_version])\n    if certfile:\n        keyfile = keyfile or certfile  # Match behaviour of the normal python ssl library\n        ctx.use_certificate_file(certfile)\n    if keyfile:\n        ctx.use_privatekey_file(keyfile)\n    if cert_reqs != ssl.CERT_NONE:\n        ctx.set_verify(_openssl_verify[cert_reqs], _verify_callback)\n    if ca_certs:\n        try:\n            ctx.load_verify_locations(ca_certs, None)\n        except OpenSSL.SSL.Error as e:\n            raise ssl.SSLError('bad ca_certs: %r' % ca_certs, e)\n    else:\n        ctx.set_default_verify_paths()\n\n    # Disable TLS compression to migitate CRIME attack (issue #309)\n    OP_NO_COMPRESSION = 0x20000\n    ctx.set_options(OP_NO_COMPRESSION)\n\n    # Set list of supported ciphersuites.\n    ctx.set_cipher_list(DEFAULT_SSL_CIPHER_LIST)\n\n    cnx = OpenSSL.SSL.Connection(ctx, sock)\n    cnx.set_tlsext_host_name(server_hostname)\n    cnx.set_connect_state()\n    while True:\n        try:\n            cnx.do_handshake()\n        except OpenSSL.SSL.WantReadError:\n            rd, _, _ = select.select([sock], [], [], sock.gettimeout())\n            if not rd:\n                raise timeout('select timed out')\n            continue\n        except OpenSSL.SSL.Error as e:\n            raise ssl.SSLError('bad handshake', e)\n        break\n\n    return WrappedSocket(cnx, sock)\n", "path": "urllib3/contrib/pyopenssl.py"}]}