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