OLD | NEW |
(Empty) | |
| 1 """A library for integrating Python's builtin ``ssl`` library with CherryPy. |
| 2 |
| 3 The ssl module must be importable for SSL functionality. |
| 4 |
| 5 To use this module, set ``CherryPyWSGIServer.ssl_adapter`` to an instance of |
| 6 ``BuiltinSSLAdapter``. |
| 7 """ |
| 8 |
| 9 try: |
| 10 import ssl |
| 11 except ImportError: |
| 12 ssl = None |
| 13 |
| 14 try: |
| 15 from _pyio import DEFAULT_BUFFER_SIZE |
| 16 except ImportError: |
| 17 try: |
| 18 from io import DEFAULT_BUFFER_SIZE |
| 19 except ImportError: |
| 20 DEFAULT_BUFFER_SIZE = -1 |
| 21 |
| 22 import sys |
| 23 |
| 24 from cherrypy import wsgiserver |
| 25 |
| 26 |
| 27 class BuiltinSSLAdapter(wsgiserver.SSLAdapter): |
| 28 """A wrapper for integrating Python's builtin ssl module with CherryPy.""" |
| 29 |
| 30 certificate = None |
| 31 """The filename of the server SSL certificate.""" |
| 32 |
| 33 private_key = None |
| 34 """The filename of the server's private key file.""" |
| 35 |
| 36 def __init__(self, certificate, private_key, certificate_chain=None): |
| 37 if ssl is None: |
| 38 raise ImportError("You must install the ssl module to use HTTPS.") |
| 39 self.certificate = certificate |
| 40 self.private_key = private_key |
| 41 self.certificate_chain = certificate_chain |
| 42 |
| 43 def bind(self, sock): |
| 44 """Wrap and return the given socket.""" |
| 45 return sock |
| 46 |
| 47 def wrap(self, sock): |
| 48 """Wrap and return the given socket, plus WSGI environ entries.""" |
| 49 try: |
| 50 s = ssl.wrap_socket(sock, do_handshake_on_connect=True, |
| 51 server_side=True, certfile=self.certificate, |
| 52 keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23) |
| 53 except ssl.SSLError: |
| 54 e = sys.exc_info()[1] |
| 55 if e.errno == ssl.SSL_ERROR_EOF: |
| 56 # This is almost certainly due to the cherrypy engine |
| 57 # 'pinging' the socket to assert it's connectable; |
| 58 # the 'ping' isn't SSL. |
| 59 return None, {} |
| 60 elif e.errno == ssl.SSL_ERROR_SSL: |
| 61 if e.args[1].endswith('http request'): |
| 62 # The client is speaking HTTP to an HTTPS server. |
| 63 raise wsgiserver.NoSSLError |
| 64 elif e.args[1].endswith('unknown protocol'): |
| 65 # The client is speaking some non-HTTP protocol. |
| 66 # Drop the conn. |
| 67 return None, {} |
| 68 raise |
| 69 return s, self.get_environ(s) |
| 70 |
| 71 # TODO: fill this out more with mod ssl env |
| 72 def get_environ(self, sock): |
| 73 """Create WSGI environ entries to be merged into each request.""" |
| 74 cipher = sock.cipher() |
| 75 ssl_environ = { |
| 76 "wsgi.url_scheme": "https", |
| 77 "HTTPS": "on", |
| 78 'SSL_PROTOCOL': cipher[1], |
| 79 'SSL_CIPHER': cipher[0] |
| 80 ## SSL_VERSION_INTERFACE string The mod_ssl program version |
| 81 ## SSL_VERSION_LIBRARY string The OpenSSL program version |
| 82 } |
| 83 return ssl_environ |
| 84 |
| 85 if sys.version_info >= (3, 0): |
| 86 def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE): |
| 87 return wsgiserver.CP_makefile(sock, mode, bufsize) |
| 88 else: |
| 89 def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE): |
| 90 return wsgiserver.CP_fileobject(sock, mode, bufsize) |
| 91 |
OLD | NEW |