Index: paste/httpserver.py
===================================================================
--- paste/httpserver.py	(revision 7326)
+++ paste/httpserver.py	(working copy)
@@ -27,7 +27,7 @@
 from itertools import count
 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
 from SocketServer import ThreadingMixIn
-from paste.util import converters
+from paste.util import converters, import_string
 import logging
 try:
     from paste.util import killthread
@@ -35,6 +35,9 @@
     # Not available, probably no ctypes
     killthread = None
 
+
+from pprint import pprint
+
 __all__ = ['WSGIHandlerMixin', 'WSGIServer', 'WSGIHandler', 'serve']
 __version__ = "0.5"
 
@@ -190,8 +193,7 @@
                 '100-continue' == self.headers.get('Expect','').lower():
             rfile = ContinueHook(rfile, self.wfile.write)
         else:
-            # We can put in the protection to keep from over-reading the
-            # file
+            # We can put in the protection to keep from over-reading the file
             try:
                 content_length = int(self.headers.get('Content-Length', '0'))
             except ValueError:
@@ -224,6 +226,9 @@
                # CGI not required by PEP-333
                ,'REMOTE_ADDR': remote_address
                }
+        if SSL:
+            self.wsgi_environ['SSL_VERSION_LIBRARY'] = __openssl_version__
+            self.wsgi_environ['SSL_CLIENT_VERIFY'] = 'NONE'
         if scheme:
             self.wsgi_environ['paste.httpserver.proxy.scheme'] = scheme
         if netloc:
@@ -254,10 +259,29 @@
                 continue
             self.wsgi_environ[key] = ','.join(self.headers.getheaders(k))
 
+
         if hasattr(self.connection,'get_context'):
-            self.wsgi_environ['wsgi.url_scheme'] = 'https'
+            env = self.wsgi_environ
+            env['wsgi.url_scheme'] = 'https'
             # @@: extract other SSL parameters from pyOpenSSL at...
             # http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25
+            client_cert = self.connection.get_peer_certificate()
+            if client_cert:
+                env['SSL_CLIENT_CERT'] = crypto.dump_certificate(
+                    crypto.FILETYPE_PEM, client_cert
+                )
+                env['SSL_CLIENT_M_VERSION'] = client_cert.get_version()
+                env['SSL_CLIENT_M_SERIAL'] = client_cert.get_serial_number()
+                try:
+                    env['SSL_CLIENT_V_START'] = client_cert.get_notBefore()
+                    env['SSL_CLIENT_V_END'] = client_cert.get_notAfter()
+                except AttributeError:
+                    # This is PyOpenSSL < 0.7
+                    pass
+                if self.connection.state_string().endswith('successfully'):
+                    env['SSL_CLIENT_VERIFY'] = 'SUCCESS'
+                else:
+                    env['SSL_CLIENT_VERIFY'] = 'FAILED'
 
         if environ:
             assert isinstance(environ, dict)
@@ -314,7 +338,7 @@
 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
 #
 try:
-    from OpenSSL import SSL, tsafe
+    from OpenSSL import SSL, tsafe, __version__ as __openssl_version__, crypto
     SocketErrors = (socket.error, SSL.ZeroReturnError, SSL.SysCallError)
 except ImportError:
     # Do not require pyOpenSSL to be installed, but disable SSL
@@ -327,6 +351,439 @@
             assert not ssl_context, "pyOpenSSL not installed"
             HTTPServer.__init__(self, server_address, RequestHandlerClass)
 else:
+    import itertools, md5
+
+    # Private - shared between all OpenSSLCertificateOptions, counts up to provide
+    # a unique session id for each context
+    _sessionCounter = itertools.count().next
+
+    class _SSLApplicationData(object):
+        def __init__(self):
+            self.problems = []
+
+
+    class OpenSSLVerifyError(Exception):
+
+        _errorCodes = {0: ('X509_V_OK',
+                           'ok',
+                           'the operation was successful. >'),
+
+                       2: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT',
+                           'unable to get issuer certificate',
+                           "The issuer certificate could not be found.  This "
+                           "occurs if the issuer certificate of an untrusted "
+                           "certificate cannot be found."),
+
+                       3: ('X509_V_ERR_UNABLE_TO_GET_CRL',
+                           'unable to get certificate CRL',
+                           "The CRL of a certificate could not be found. "
+                           "Unused."),
+
+                       4: ('X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE',
+                           "unable to decrypt certificate's signature",
+                           "The certificate signature could not be decrypted.  "
+                           "This means that the actual signature value could not "
+                           "be determined rather than it not matching the "
+                           "expected value, this is only meaningful for RSA "
+                           "keys."),
+
+                       5: ('X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE',
+                           "unable to decrypt CRL's signature",
+                           "The CRL signature could not be decrypted.  This "
+                           "means that the actual signature value could not be "
+                           "determined rather than it not matching the expected "
+                           "value. Unused."),
+
+                       6: ('X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY',
+                           'unable to decode issuer',
+                           "Public key the public key in the certificate "
+                           "SubjectPublicKeyInfo could not be read."),
+
+                       7: ('X509_V_ERR_CERT_SIGNATURE_FAILURE',
+                           'certificate signature failure',
+                           'The signature of the certificate is invalid.'),
+
+                       8: ('X509_V_ERR_CRL_SIGNATURE_FAILURE',
+                           'CRL signature failure',
+                           'The signature of the certificate is invalid. Unused.'),
+
+                       9: ('X509_V_ERR_CERT_NOT_YET_VALID',
+                           'certificate is not yet valid',
+                           "The certificate is not yet valid.  The notBefore "
+                           "date is after the current time."),
+
+                       10: ('X509_V_ERR_CERT_HAS_EXPIRED',
+                            'certificate has expired',
+                            "The certificate has expired.  The notAfter date "
+                            "is before the current time."),
+
+                       11: ('X509_V_ERR_CRL_NOT_YET_VALID',
+                            'CRL is not yet valid',
+                            'The CRL is not yet valid. Unused.'),
+
+                       12: ('X509_V_ERR_CRL_HAS_EXPIRED',
+                            'CRL has expired',
+                            'The CRL has expired. Unused.'),
+
+                       13: ('X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD',
+                            "format error in certificate's notBefore field",
+                            "The certificate's notBefore field contains an "
+                            "invalid time."),
+
+                       14: ('X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD',
+                            "format error in certificate's notAfter field.",
+                            "The certificate's notAfter field contains an "
+                            "invalid time."),
+
+                       15: ('X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD',
+                            "format error in CRL's lastUpdate field",
+                            "The CRL lastUpdate field contains an invalid "
+                            "time. Unused."),
+
+                       16: ('X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD',
+                            "format error in CRL's nextUpdate field",
+                            "The CRL nextUpdate field contains an invalid "
+                            "time. Unused."),
+
+                       17: ('X509_V_ERR_OUT_OF_MEM',
+                            'out of memory',
+                            'An error occurred trying to allocate memory. '
+                            'This should never happen.'),
+
+                       18: ('X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT',
+                            'self signed certificate',
+                            'The passed certificate is self signed and the same '
+                            'certificate cannot be found in the list of trusted '
+                            'certificates.'),
+
+                       19: ('X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN',
+                            'self signed certificate in certificate chain',
+                            'The certificate chain could be built up using the '
+                            'untrusted certificates but the root could not be '
+                            'found locally.'),
+
+                       20: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
+                            'unable to get local issuer certificate',
+                            'The issuer certificate of a locally looked up '
+                            'certificate could not be found. This normally '
+                            'means the list of trusted certificates is not '
+                            'complete.'),
+
+                       21: ('X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE',
+                            'unable to verify the first certificate',
+                            'No signatures could be verified because the chain '
+                            'contains only one certificate and it is not self '
+                            'signed.'),
+
+                       22: ('X509_V_ERR_CERT_CHAIN_TOO_LONG',
+                            'certificate chain too long',
+                            'The certificate chain length is greater than the '
+                            'supplied maximum depth. Unused.'),
+
+                       23: ('X509_V_ERR_CERT_REVOKED',
+                            'certificate revoked',
+                            'The certificate has been revoked. Unused.'),
+
+                       24: ('X509_V_ERR_INVALID_CA',
+                            'invalid CA certificate',
+                            'A CA certificate is invalid. Either it is not a CA '
+                            'or its extensions are not consistent with the '
+                            'supplied purpose.'),
+
+                       25: ('X509_V_ERR_PATH_LENGTH_EXCEEDED',
+                            'path length constraint exceeded',
+                            'The basicConstraints pathlength parameter has been '
+                            'exceeded.'),
+
+                       26: ('X509_V_ERR_INVALID_PURPOSE',
+                            'unsupported certificate purpose',
+                            'The supplied certificate cannot be used for the '
+                            'specified purpose.'),
+
+                       27: ('X509_V_ERR_CERT_UNTRUSTED',
+                            'certificate not trusted',
+                            'The root CA is not marked as trusted for the '
+                            'specified purpose.'),
+
+                       28: ('X509_V_ERR_CERT_REJECTED',
+                            'certificate rejected',
+                            'The root CA is marked to reject the specified '
+                            'purpose.'),
+
+                       29: ('X509_V_ERR_SUBJECT_ISSUER_MISMATCH',
+                            'subject issuer mismatch',
+                            'The current candidate issuer certificate was '
+                            'rejected because its subject name did not match '
+                            'the issuer name of the current certificate. Only '
+                            'displayed when the issuer_checks option is set.'),
+
+                       30: ('X509_V_ERR_AKID_SKID_MISMATCH',
+                            'authority and subject key identifier mismatch',
+                            'The current candidate issuer certificate was '
+                            'rejected because its subject key identifier was '
+                            'present and did not match the authority key '
+                            'identifier current certificate. Only displayed '
+                            'when the issuer_checks option is set.'),
+
+                       31: ('X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH',
+                            'authority and issuer serial number mismatch',
+                            'The current candidate issuer certificate was '
+                            'rejected because its issuer name and serial '
+                            'number was present and did not match the '
+                            'authority key identifier of the current '
+                            'certificate. Only displayed when the issuer_checks '
+                            'option is set.'),
+
+                       32: ('X509_V_ERR_KEYUSAGE_NO_CERTSIGN',
+                            'key usage does not include certificate',
+                            'Signing the current candidate issuer certificate was '
+                            'rejected because its keyUsage extension does not '
+                            'permit certificate signing.'),
+
+                       50: ('X509_V_ERR_APPLICATION_VERIFICATION',
+                            'application verification failure',
+                            'an application specific error. Unused.')}
+
+
+        def __init__(self, cert, errno, depth):
+            Exception.__init__(self, cert, errno, depth)
+            self.cert = cert
+            self.errno = errno
+            self.depth = depth
+
+        def __repr__(self):
+            x = self._errorCodes.get(self.errno)
+            if x is not None:
+                name, short, long = x
+                return 'Peer Certificate Verification Failed: %s (error code: %d)' \
+                    % ( long, self.errno )
+            return "Peer Certificate Verification Failed for Unknown Reason"
+
+        __str__ = __repr__
+
+
+    class OpenSSLCertificateOptions(object):
+        """
+        A factory for SSL context objects for both SSL servers and clients.
+        """
+
+        _context = None
+        # Older versions of PyOpenSSL didn't provide OP_ALL.
+        # Fudge it here, just in case.
+        _OP_ALL = getattr(SSL, 'OP_ALL', 0x0000FFFF)
+
+        method = SSL.SSLv23_METHOD
+
+        def __init__(self,
+                     privateKey=None,
+                     privateKeyPasswd=None,
+                     certificate=None,
+                     method=None,
+                     verify=False,
+                     caCerts=None,
+                     verifyDepth=9,
+                     requireCertificate=True,
+                     verifyOnce=True,
+                     enableSingleUseKeys=True,
+                     enableSessions=True,
+                     fixBrokenPeers=False):
+            """
+            Create an OpenSSL context SSL connection context factory.
+
+            ``ssl_privateKey``
+
+                private key file path or PKey object.
+
+            ``ssl_privateKeyPasswd``
+
+                A string holding the ssl_privateKey's password.
+
+            ``ssl_certificate``
+
+                This an optional SSL certificate file path (via OpenSSL) or
+                X509 object. You can supply ``*`` and a development-only
+                certificate will be created for you, or you can generate a
+                self-signed test PEM certificate file as follows::
+
+                    $ openssl genrsa 1024 > host.key
+                    $ chmod 400 host.key
+                    $ openssl req -new -x509 -nodes -sha1 -days 365  \\
+                                  -key host.key > host.cert
+                    $ cat host.cert host.key > host.pem
+                    $ chmod 400 host.pem
+
+            ``ssl_method``
+
+                The SSL protocol to use, one of SSLv23_METHOD, SSLv2_METHOD,
+                SSLv3_METHOD, TLSv1_METHOD.  Defaults to SSLv23_METHOD.
+
+            ``ssl_verify``
+
+                If True, verify certificates received from the peer and fail the
+                handshake if verification fails.  Otherwise, allow anonymous
+                sessions and sessions with certificates which fail validation.
+                By default this is False.
+
+            ``ssl_caCerts``
+
+                List of certificate authority certificates to send to the client
+                when requesting a certificate. Only used if verify is True.
+                Since verify is False by default, this is None by default.
+
+            ``ssl_verifyDepth``
+
+                Depth in certificate chain down to which to verify.
+                If unspecified, use the underlying default (9).
+
+            ``ssl_requireCertificate``
+
+                If True, do not allow anonymous sessions.
+
+            ``ssl_verifyOnce``
+
+                If True, do not re-verify the certificate on session resumption.
+
+            ``ssl_enableSingleUseKeys``
+
+                If True, generate a new key whenever ephemeral DH parameters are
+                used to prevent small subgroup attacks.
+
+            ``ssl_enableSessions``
+
+                If True, set a session ID on each context.  This allows a
+                shortened handshake to be used when a known client reconnects.
+
+            ``fixBrokenPeers``
+
+                If True, enable various non-spec protocol fixes for broken SSL
+                implementations.  This should be entirely safe, according to the
+                OpenSSL documentation, but YMMV. This option is now off by
+                default, because it causes problems with connections between
+                peers using OpenSSL 0.9.8a.
+            """
+
+            assert (privateKey is None) == (certificate is None), \
+                "Specify neither or both of privateKey and certificate"
+            self.privateKey = privateKey
+            self.privateKeyPasswd = privateKeyPasswd
+            self.certificate = certificate
+            if method is not None:
+                if method.lower().startswith('sslv23'):
+                    self.method = SSL.SSLv23_METHOD
+                if method.lower().startswith('sslv2'):
+                    self.method = SSL.SSLv2_METHOD
+                elif method.lower().startswith('sslv3'):
+                    self.method = SSL.SSLv3_METHOD
+                elif method.lower().startswith('tlsv1'):
+                    self.method = SSL.TLSv1_METHOD
+            else:
+                self.method = SSL.SSLv23_METHOD
+
+            self.verify = verify
+            assert ((verify and caCerts) or
+                    (not verify)), "Specify client CA certificate information" \
+                                   " if and only if enabling certificate " \
+                                   "verification"
+
+            if caCerts:
+                certs = []
+                for caCert in caCerts:
+                    if isinstance(caCert, basestring):
+                        certs.append(
+                            crypto.load_certificate(crypto.FILETYPE_PEM,
+                                                    open(caCert).read())
+                        )
+                self.caCerts = certs
+            else:
+                self.caCerts = caCerts
+            self.verifyDepth = verifyDepth
+            self.requireCertificate = requireCertificate
+            self.verifyOnce = verifyOnce
+            self.enableSingleUseKeys = enableSingleUseKeys
+            self.enableSessions = enableSessions
+            self.fixBrokenPeers = fixBrokenPeers
+
+
+        def __getstate__(self):
+            d = self.__dict__.copy()
+            try:
+                del d['_context']
+            except KeyError:
+                pass
+            return d
+
+
+        def __setstate__(self, state):
+            self.__dict__ = state
+
+
+        def getContext(self):
+            """Return a SSL.Context object.
+            """
+            if self._context is None:
+                self._context = self._makeContext()
+            return self._context
+
+
+        def _makeContext(self):
+            ctx = SSL.Context(self.method)
+            ctx.set_app_data(_SSLApplicationData())
+
+            if self.privateKeyPasswd:
+                ctx.set_passwd_cb(lambda a, b, c: self.privateKeyPasswd)
+
+            if self.certificate is not None and self.privateKey is not None:
+                try:
+                    ctx.use_certificate(self.certificate)
+                except:
+                    # Passed a filepath?
+                    ctx.use_certificate_file(self.certificate)
+                try:
+                    ctx.use_privatekey(self.privateKey)
+                except:
+                    # Passed a filepath?
+                    ctx.use_privatekey_file(self.privateKey)
+                # Sanity check
+                ctx.check_privatekey()
+
+            verifyFlags = SSL.VERIFY_NONE
+            if self.verify:
+                verifyFlags = SSL.VERIFY_PEER
+                if self.requireCertificate:
+                    verifyFlags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT
+                if self.verifyOnce:
+                    verifyFlags |= SSL.VERIFY_CLIENT_ONCE
+                if self.caCerts:
+                    store = ctx.get_cert_store()
+                    for cert in self.caCerts:
+                        store.add_cert(cert)
+                self.verifyFlags = verifyFlags
+
+            def _trackVerificationProblems(conn,cert,errno,depth,preverify_ok):
+                # retcode is the answer OpenSSL's default verifier would have
+                # given, had we allowed it to run.
+                if not preverify_ok:
+                    error = OpenSSLVerifyError(cert, errno, depth)
+                    ctx.get_app_data().problems.append(error)
+                return preverify_ok
+            ctx.set_verify(verifyFlags, _trackVerificationProblems)
+
+            if self.verifyDepth is not None:
+                ctx.set_verify_depth(self.verifyDepth)
+
+            if self.enableSingleUseKeys:
+                ctx.set_options(SSL.OP_SINGLE_DH_USE)
+
+            if self.fixBrokenPeers:
+                ctx.set_options(self._OP_ALL)
+
+            if self.enableSessions:
+                sessionName = md5.md5("%s-%d" % (
+                    self.__class__.__module__ + '.' + self.__class__.__name__,
+                    _sessionCounter())).hexdigest()
+                ctx.set_session_id(sessionName)
+
+            return ctx
 
     class _ConnFixer(object):
         """ wraps a socket connection so it implements makefile """
@@ -380,28 +837,6 @@
                 conn = _ConnFixer(conn)
             return (conn, info)
 
-    def _auto_ssl_context():
-        import OpenSSL, time, random
-        pkey = OpenSSL.crypto.PKey()
-        pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 768)
-
-        cert = OpenSSL.crypto.X509()
-
-        cert.set_serial_number(random.randint(0, sys.maxint))
-        cert.gmtime_adj_notBefore(0)
-        cert.gmtime_adj_notAfter(60 * 60 * 24 * 365)
-        cert.get_subject().CN = '*'
-        cert.get_subject().O = 'Dummy Certificate'
-        cert.get_issuer().CN = 'Untrusted Authority'
-        cert.get_issuer().O = 'Self-Signed'
-        cert.set_pubkey(pkey)
-        cert.sign(pkey, 'md5')
-
-        ctx = SSL.Context(SSL.SSLv23_METHOD)
-        ctx.use_privatekey(pkey)
-        ctx.use_certificate(cert)
-
-        return ctx
 
 class WSGIHandler(WSGIHandlerMixin, BaseHTTPRequestHandler):
     """
@@ -424,12 +859,20 @@
             return
         if not self.parse_request(): # An error code has been sent, just exit
             return
+        if self.headers.get('x-renegotiate-ssl', None):
+            # If the response to the client contains X-Renegotiate-SSL header
+            # the SSL connection is re-negotiated.
+            self.connection.renegotiate()
         self.wsgi_execute()
 
     def handle(self):
         # don't bother logging disconnects while handling a request
         try:
             BaseHTTPRequestHandler.handle(self)
+        except (SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError):
+            pass
+        except SSL.Error, error:
+            self.wsgi_connection_drop(error)
         except SocketErrors, exce:
             self.wsgi_connection_drop(exce)
 
@@ -456,7 +899,7 @@
             length = left
         else:
             length = min(length, left)
-        # next two lines are hnecessary only if read(0) blocks
+        # next two lines are necessary only if read(0) blocks
         if not left:
             return ''
         data = self.file.read(length)
@@ -1126,11 +1569,11 @@
     caught)
     """
 
-def serve(application, host=None, port=None, handler=None, ssl_pem=None,
-          ssl_context=None, server_version=None, protocol_version=None,
-          start_loop=True, daemon_threads=None, socket_timeout=None,
-          use_threadpool=None, threadpool_workers=10,
-          threadpool_options=None):
+
+def serve(application, host=None, port=None, handler=None, ssl_options=None,
+          server_version=None, protocol_version=None, start_loop=True,
+          daemon_threads=None, socket_timeout=None, use_threadpool=None,
+          threadpool_workers=10, threadpool_options=None):
     """
     Serves your ``application`` over HTTP(S) via WSGI interface
 
@@ -1164,12 +1607,7 @@
             $ cat host.cert host.key > host.pem
             $ chmod 400 host.pem
 
-    ``ssl_context``
 
-        This an optional SSL context object for the server.  A SSL
-        context will be automatically constructed for you if you supply
-        ``ssl_pem``.  Supply this to use a context of your own
-        construction.
 
     ``server_version``
 
@@ -1226,19 +1664,105 @@
         threadpool.  See paste.httpserver.ThreadPool for specific
         options (``threadpool_workers`` is a specific option that can
         also go here).
+
+    ``ssl_privateKey``
+
+        private key file path.
+
+    ``ssl_privateKeyPasswd``
+
+        A string holding the ssl_privateKey's password.
+
+    ``ssl_certificate``
+
+        This an optional SSL certificate file (via OpenSSL). You can
+        supply ``*`` and a development-only certificate will be
+        created for you, or you can generate a self-signed test PEM
+        certificate file as follows::
+
+            $ openssl genrsa 1024 > host.key
+            $ chmod 400 host.key
+            $ openssl req -new -x509 -nodes -sha1 -days 365  \\
+                          -key host.key > host.cert
+            $ cat host.cert host.key > host.pem
+            $ chmod 400 host.pem
+
+    ``ssl_method``
+
+        The SSL protocol to use, one of SSLv23_METHOD, SSLv2_METHOD,
+        SSLv3_METHOD, TLSv1_METHOD.  Defaults to SSLv23_METHOD.
+
+    ``ssl_verify``
+
+        If True, verify certificates received from the peer and fail the
+        handshake if verification fails.  Otherwise, allow anonymous sessions
+        and sessions with certificates which fail validation.  By default this
+        is False.
+
+    ``ssl_caCerts``
+
+        List of certificate authority certificates to send to the client when
+        requesting a certificate.  Only used if verify is True.  Since verify
+        is False by default, this is None by default.
+
+    ``ssl_verifyDepth``
+
+        Depth in certificate chain down to which to verify.
+        If unspecified, use the underlying default (9).
+
+    ``ssl_requireCertificate``
+
+        If True, do not allow anonymous sessions.
+
+    ``ssl_verifyOnce``
+
+        If True, do not re-verify the certificate on session resumption.
+
+    ``ssl_enableSingleUseKeys``
+
+        If True, generate a new key whenever ephemeral DH parameters are used
+        to prevent small subgroup attacks.
+
+    ``ssl_enableSessions``
+
+        If True, set a session ID on each context.  This allows a shortened
+        handshake to be used when a known client reconnects.
+
+    ``fixBrokenPeers``
+
+        If True, enable various non-spec protocol fixes for broken SSL
+        implementations.  This should be entirely safe, according to the OpenSSL
+        documentation, but YMMV.  This option is now off by default, because it
+        causes problems with connections between peers using OpenSSL 0.9.8a.
+
     """
     is_ssl = False
-    if ssl_pem or ssl_context:
+    if ssl_options:
         assert SSL, "pyOpenSSL is not installed"
         is_ssl = True
         port = int(port or 4443)
-        if not ssl_context:
-            if ssl_pem == '*':
-                ssl_context = _auto_ssl_context()
-            else:
-                ssl_context = SSL.Context(SSL.SSLv23_METHOD)
-                ssl_context.use_privatekey_file(ssl_pem)
-                ssl_context.use_certificate_chain_file(ssl_pem)
+        if ssl_options.get('certificate') == '*':
+            from OpenSSL import crypto
+            import random
+            pkey = crypto.PKey()
+            pkey.generate_key(crypto.TYPE_RSA, 768)
+
+            cert = crypto.X509()
+
+            cert.set_serial_number(random.randint(0, sys.maxint))
+            cert.gmtime_adj_notBefore(0)
+            cert.gmtime_adj_notAfter(60 * 60 * 24 * 365)
+            cert.get_subject().CN = '*'
+            cert.get_subject().O = 'Dummy Certificate'
+            cert.get_issuer().CN = 'Untrusted Authority'
+            cert.get_issuer().O = 'Self-Signed'
+            cert.set_pubkey(pkey)
+            cert.sign(pkey, 'md5')
+            ssl_options['privateKey'] = pkey
+            ssl_options['certificate'] = cert
+
+        ctxopts = OpenSSLCertificateOptions(**ssl_options)
+        ssl_context = ctxopts.getContext()
 
     host = host or '127.0.0.1'
     if port is None:
@@ -1305,15 +1829,30 @@
     for name in ['use_threadpool', 'daemon_threads']:
         if name in kwargs:
             kwargs[name] = asbool(kwargs[name])
+    ssl_options = {}
     threadpool_options = {}
     for name, value in kwargs.items():
         if name.startswith('threadpool_') and name != 'threadpool_workers':
             threadpool_options[name[len('threadpool_'):]] = value
             del kwargs[name]
+        elif name.startswith('ssl_'):
+            ctxopt = name[len('ssl_'):]
+            if ctxopt in ('verify', 'requireCertificate', 'verifyOnce',
+                          'enableSingleUseKeys', 'enableSessions',
+                          'fixBrokenPeers'):
+                ssl_options[ctxopt] = asbool(value)
+            elif ctxopt == 'verifyDepth':
+                ssl_options[ctxopt] = int(value)
+            elif ctxopt == 'caCerts':
+                ssl_options[ctxopt] = converters.aslist(value)
+            else:
+                ssl_options[ctxopt] = value
+            del kwargs[name]
     if ('error_email' not in threadpool_options
         and 'error_email' in global_conf):
         threadpool_options['error_email'] = global_conf['error_email']
     kwargs['threadpool_options'] = threadpool_options
+    kwargs['ssl_options'] = ssl_options
     serve(wsgi_app, **kwargs)
 
 server_runner.__doc__ = (serve.__doc__ or '') + """

