Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 1345

Show
Ignore:
Timestamp:
09/09/06 22:47:10
Author:
fumanchu
Message:

SSL support for _cpwsgiserver. One test fails immaterially. Run "test.py --ssl" to test.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/_cpserver.py

    r1325 r1345  
    5151    max_request_body_size = 100 * 1024 * 1024 
    5252    instance = None 
     53    ssl_certificate = None 
     54    ssl_private_key = None 
    5355     
    5456    def __init__(self): 
  • trunk/cherrypy/_cpwsgi.py

    r1310 r1345  
    138138                   ) 
    139139        s.protocol = server.protocol_version 
     140        s.ssl_certificate = server.ssl_certificate 
     141        s.ssl_private_key = server.ssl_private_key 
    140142 
  • trunk/cherrypy/_cpwsgiserver.py

    r1344 r1345  
    1818from urllib import unquote 
    1919from urlparse import urlparse 
     20 
     21try: 
     22    from OpenSSL import SSL 
     23except ImportError: 
     24    SSL = None 
    2025 
    2126import errno 
     
    342347 
    343348 
     349def _ssl_wrap_method(method): 
     350    def ssl_method_wrapper(self, *args, **kwargs): 
     351##        print (id(self), method, args, kwargs) 
     352        while True: 
     353            try: 
     354                return method(self, *args, **kwargs) 
     355            except (SSL.WantReadError, SSL.WantWriteError): 
     356                # Sleep and try again 
     357                time.sleep(self.ssl_retry) 
     358            except SSL.SysCallError, e: 
     359                errno = e.args[0] 
     360                if errno not in socket_errors_to_ignore: 
     361                    raise socket.error(errno) 
     362                return "" 
     363            except SSL.Error, e: 
     364                if e.args == (-1, 'Unexpected EOF'): 
     365                    return "" 
     366                elif e.args[0][0][2] == 'ssl handshake failure': 
     367                    return "" 
     368                else: 
     369                    raise 
     370##        raise socket.timeout() 
     371    return ssl_method_wrapper 
     372 
     373class SSL_fileobject(socket._fileobject): 
     374    """Faux file object attached to a socket object.""" 
     375     
     376    ssl_timeout = 3 
     377    ssl_retry = .01 
     378     
     379    close = _ssl_wrap_method(socket._fileobject.close) 
     380    flush = _ssl_wrap_method(socket._fileobject.flush) 
     381    write = _ssl_wrap_method(socket._fileobject.write) 
     382    writelines = _ssl_wrap_method(socket._fileobject.writelines) 
     383    read = _ssl_wrap_method(socket._fileobject.read) 
     384    readline = _ssl_wrap_method(socket._fileobject.readline) 
     385    readlines = _ssl_wrap_method(socket._fileobject.readlines) 
     386 
     387 
    344388class HTTPConnection(object): 
    345389     
    346     bufsize = -1 
     390    rbufsize = -1 
     391    wbufsize = -1 
    347392    RequestHandlerClass = HTTPRequest 
    348393    environ = {"wsgi.version": (1, 0), 
     
    354399               } 
    355400     
    356     def __init__(self, socket, addr, server): 
    357         self.socket = socket 
     401    def __init__(self, sock, addr, server): 
     402        self.socket = sock 
    358403        self.addr = addr 
    359404        self.server = server 
    360405         
    361         self.rfile = self.socket.makefile("r", self.bufsize) 
    362         self.wfile = self.socket.makefile("w", self.bufsize) 
    363          
    364406        # Copy the class environ into self. 
    365407        self.environ = self.environ.copy() 
     408         
     409        if type(sock) is socket.socket: 
     410            self.rfile = self.socket.makefile("r", self.rbufsize) 
     411            self.wfile = self.socket.makefile("w", self.wbufsize) 
     412        else: 
     413            # Assume it's an HTTPS socket wrapper 
     414            self.environ["wsgi.url_scheme"] = "https" 
     415            self.rfile = SSL_fileobject(sock, "r", self.rbufsize) 
     416            self.wfile = SSL_fileobject(sock, "w", self.wbufsize) 
     417         
    366418        self.environ.update({"wsgi.input": self.rfile, 
    367419                             "SERVER_NAME": self.server.server_name, 
     
    391443                req.parse_request() 
    392444                if not req.ready: 
    393                     break 
     445                    return 
    394446                req.respond() 
    395447                if req.close_connection: 
    396                     break 
     448                    return 
    397449        except socket.error, e: 
    398450            errno = e.args[0] 
     
    401453                    req.simple_response("500 Internal Server Error", 
    402454                                        format_exc()) 
     455            return 
    403456        except (KeyboardInterrupt, SystemExit): 
    404457            raise 
     
    469522    ConnectionClass = HTTPConnection 
    470523     
     524    # Paths to certificate and private key files 
     525    ssl_certificate = None 
     526    ssl_private_key = None 
     527     
    471528    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, 
    472529                 max=-1, request_queue_size=5, timeout=10): 
     
    507564            self.socket = socket.socket(family, type, proto) 
    508565            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     566            if self.ssl_certificate and self.ssl_private_key: 
     567                # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473 
     568                ctx = SSL.Context(SSL.SSLv23_METHOD) 
     569                ctx.use_privatekey_file(self.ssl_private_key) 
     570                ctx.use_certificate_file(self.ssl_certificate) 
     571                self.socket = SSL.Connection(ctx, self.socket) 
    509572            self.socket.bind(self.bind_addr) 
    510573         
  • trunk/cherrypy/test/modpy.py

    r1326 r1345  
    133133        webtest.WebCase.PORT = self.port 
    134134        webtest.WebCase.harness = self 
     135        webtest.WebCase.scheme = "http" 
    135136        print 
    136137        print "Running tests:", self.server 
  • trunk/cherrypy/test/test.py

    r1343 r1345  
    1111 
    1212 
     13import getopt 
     14import httplib 
     15import os, os.path 
     16localDir = os.path.dirname(__file__) 
     17serverpem = os.path.join(os.getcwd(), localDir, 'test.pem') 
    1318import sys 
    14 import os, os.path 
    15 import getopt 
    1619 
    1720 
     
    1922    """A test harness for the CherryPy framework and CherryPy applications.""" 
    2023     
    21     def __init__(self, tests=None, server=None, protocol="HTTP/1.1", port=8000): 
     24    def __init__(self, tests=None, server=None, protocol="HTTP/1.1", 
     25                 port=8000, scheme="http"): 
    2226        """Constructor to populate the TestHarness instance. 
    2327         
    2428        tests should be a list of module names (strings). 
    2529        """ 
     30        self.tests = tests or [] 
     31        self.server = server 
    2632        self.protocol = protocol 
    2733        self.port = port 
    28         self.server = server 
    29         self.tests = tests or [] 
     34        self.scheme = scheme 
    3035     
    3136    def run(self, conf=None): 
     
    3540        print "Python version used to run this test script:", v 
    3641        print "CherryPy version", cherrypy.__version__ 
    37         print "HTTP server version", self.protocol 
     42        if self.scheme == "https": 
     43            ssl = "(ssl)" 
     44        else: 
     45            ssl = "" 
     46        print "HTTP server version", self.protocol, ssl 
    3847        print 
    3948         
     
    4857         
    4958        baseconf['server.protocol_version'] = self.protocol 
     59        if self.scheme == "https": 
     60            baseconf['server.ssl_certificate'] = serverpem 
     61            baseconf['server.ssl_private_key'] = serverpem 
    5062        self._run(baseconf) 
    5163     
     
    6072        webtest.WebCase.PORT = self.port 
    6173        webtest.WebCase.harness = self 
     74        webtest.WebCase.scheme = self.scheme 
     75        if self.scheme == "https": 
     76            webtest.WebCase.HTTP_CONN = httplib.HTTPSConnection 
    6277        print 
    6378        print "Running tests:", self.server 
     
    7186                         } 
    7287    default_server = "wsgi" 
     88    scheme = "http" 
     89    protocol = "HTTP/1.1" 
    7390    port = 8080 
     91    cover = False 
     92    profile = False 
     93    validate = False 
     94    server = None 
    7495    basedir = None 
    7596     
     
    83104        """ 
    84105        self.available_tests = available_tests 
    85         self.cover = False 
    86         self.profile = False 
    87         self.validate = False 
    88         self.server = None 
    89         self.protocol = "HTTP/1.1" 
    90          
    91         longopts = ['cover', 'profile', 'validate', '1.0', 'help', 'basedir=', 'port=', 
    92                     'server='] 
     106         
     107        longopts = ['cover', 'profile', 'validate', '1.0', 'ssl', 'help', 
     108                    'basedir=', 'port=', 'server='] 
    93109        longopts.extend(self.available_tests) 
    94110        try: 
     
    113129            elif o == "--1.0": 
    114130                self.protocol = "HTTP/1.0" 
     131            elif o == "--ssl": 
     132                self.scheme = "https" 
    115133            elif o == "--basedir": 
    116134                self.basedir = a 
     
    217235        if basedir is None: 
    218236            # Assume we want to cover everything in "../../cherrypy/" 
    219             localDir = os.path.dirname(__file__) 
    220237            basedir = os.path.normpath(os.path.join(os.getcwd(), localDir, '../')) 
    221238        else: 
     
    284301            h.use_wsgi = True 
    285302        else: 
    286             h = TestHarness(self.tests, self.server, self.protocol, self.port) 
     303            h = TestHarness(self.tests, self.server, self.protocol, 
     304                            self.port, self.scheme) 
    287305         
    288306        h.run(conf) 
     
    300318    # Place this __file__'s grandparent (../../) at the start of sys.path, 
    301319    # so that all cherrypy/* imports are from this __file__'s package. 
    302     localDir = os.path.dirname(__file__) 
    303320    curpath = os.path.normpath(os.path.join(os.getcwd(), localDir)) 
    304321    grandparent = os.path.normpath(os.path.join(curpath, '../../')) 
  • trunk/cherrypy/test/test_conn.py

    r1326 r1345  
    5858         
    5959        # Set our HTTP_CONN to an instance so it persists between requests. 
    60         self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 
     60        if self.scheme == "https": 
     61            self.HTTP_CONN = httplib.HTTPSConnection(self.HOST, self.PORT) 
     62        else: 
     63            self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 
    6164        # Don't automatically re-connect 
    6265        self.HTTP_CONN.auto_open = False 
     
    9497         
    9598        # Test client-side close. 
    96         self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 
     99        if self.scheme == "https": 
     100            self.HTTP_CONN = httplib.HTTPSConnection(self.HOST, self.PORT) 
     101        else: 
     102            self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 
    97103        self.HTTP_CONN.auto_open = False 
    98104        self.HTTP_CONN.connect() 
     
    123129             
    124130            # Make an initial request 
    125             conn = httplib.HTTPConnection(self.HOST, self.PORT) 
     131            if self.scheme == "https": 
     132                conn = httplib.HTTPSConnection(self.HOST, self.PORT) 
     133            else: 
     134                conn = httplib.HTTPConnection(self.HOST, self.PORT) 
    126135            conn.auto_open = False 
    127136            conn.connect() 
     
    162171            else: 
    163172                self.fail("Writing to timed out socket didn't fail" 
    164                           " as it should have.") 
     173                          " as it should have: %s" % 
     174                          response.read()) 
    165175             
    166176            conn.close() 
    167177             
    168178            # Make another request on a new socket, which should work 
    169             conn = httplib.HTTPConnection(self.HOST, self.PORT) 
     179            if self.scheme == "https": 
     180                conn = httplib.HTTPSConnection(self.HOST, self.PORT) 
     181            else: 
     182                conn = httplib.HTTPConnection(self.HOST, self.PORT) 
    170183            conn.auto_open = False 
    171184            conn.connect() 
     
    190203         
    191204        # Test pipelining. httplib doesn't support this directly. 
    192         conn = httplib.HTTPConnection(self.HOST, self.PORT) 
     205        if self.scheme == "https": 
     206            conn = httplib.HTTPSConnection(self.HOST, self.PORT) 
     207        else: 
     208            conn = httplib.HTTPConnection(self.HOST, self.PORT) 
    193209        conn.auto_open = False 
    194210        conn.connect() 
     
    227243        self.PROTOCOL = "HTTP/1.1" 
    228244         
    229         conn = httplib.HTTPConnection(self.HOST, self.PORT) 
     245        if self.scheme == "https": 
     246            conn = httplib.HTTPSConnection(self.HOST, self.PORT) 
     247        else: 
     248            conn = httplib.HTTPConnection(self.HOST, self.PORT) 
    230249        conn.auto_open = False 
    231250        conn.connect() 
     
    286305         
    287306        # Set our HTTP_CONN to an instance so it persists between requests. 
    288         conn = httplib.HTTPConnection(self.HOST, self.PORT) 
     307        if self.scheme == "https": 
     308            conn = httplib.HTTPSConnection(self.HOST, self.PORT) 
     309        else: 
     310            conn = httplib.HTTPConnection(self.HOST, self.PORT) 
    289311         
    290312        # Try a normal chunked request 
     
    323345    def test_HTTP10(self): 
    324346        self.PROTOCOL = "HTTP/1.0" 
    325         self.HTTP_CONN = httplib.HTTPConnection 
     347        if self.scheme == "https": 
     348            self.HTTP_CONN = httplib.HTTPSConnection 
     349        else: 
     350            self.HTTP_CONN = httplib.HTTPConnection 
    326351         
    327352        # Test a normal HTTP/1.0 request. 
     
    333358         
    334359        # Test a keep-alive HTTP/1.0 request. 
    335         self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 
     360        if self.scheme == "https": 
     361            self.HTTP_CONN = httplib.HTTPSConnection(self.HOST, self.PORT) 
     362        else: 
     363            self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 
    336364        self.HTTP_CONN.auto_open = False 
    337365        self.HTTP_CONN.connect() 
  • trunk/cherrypy/test/test_core.py

    r1329 r1345  
    513513        self.getPage("/redirect?id=3") 
    514514        self.assertStatus(('302 Found', '303 See Other')) 
    515         self.assertInBody("<a href='http://127.0.0.1:%s%s/redirect/?id=3'>" 
    516                           "http://127.0.0.1:%s%s/redirect/?id=3</a>" % 
    517                           (self.PORT, self.prefix(), self.PORT, self.prefix())) 
     515        self.assertInBody("<a href='%s://127.0.0.1:%s%s/redirect/?id=3'>" 
     516                          "%s://127.0.0.1:%s%s/redirect/?id=3</a>" % 
     517                          (self.scheme, self.PORT, self.prefix(), 
     518                           self.scheme, self.PORT, self.prefix())) 
    518519         
    519520        if self.prefix(): 
     
    522523            self.getPage("") 
    523524            self.assertStatus(('302 Found', '303 See Other')) 
    524             self.assertInBody("<a href='http://127.0.0.1:%s%s/'>" 
    525                               "http://127.0.0.1:%s%s/</a>" % 
    526                               (self.PORT, self.prefix(), self.PORT, self.prefix())) 
     525            self.assertInBody("<a href='%s://127.0.0.1:%s%s/'>" 
     526                              "%s://127.0.0.1:%s%s/</a>" % 
     527                              (self.scheme, self.PORT, self.prefix(), 
     528                               self.scheme, self.PORT, self.prefix())) 
    527529         
    528530        self.getPage("/redirect/by_code?code=300") 
     
    593595        self.getPage("/redirect/stringify", protocol="HTTP/1.0") 
    594596        self.assertStatus(200) 
    595         self.assertBody("(['http://127.0.0.1:%s/'], 302)" % self.PORT
     597        self.assertBody("(['%s://127.0.0.1:%s/'], 302)" % (self.scheme, self.PORT)
    596598        if cherrypy.server.protocol_version == "HTTP/1.1": 
    597599            self.getPage("/redirect/stringify", protocol="HTTP/1.1") 
    598600            self.assertStatus(200) 
    599             self.assertBody("(['http://127.0.0.1:%s/'], 303)" % self.PORT
     601            self.assertBody("(['%s://127.0.0.1:%s/'], 303)" % (self.scheme, self.PORT)
    600602     
    601603    def testFlatten(self): 
  • trunk/cherrypy/test/test_objectmapping.py

    r1342 r1345  
    182182            self.getPage("/dir1/dir2") 
    183183            self.assertStatus((302, 303)) 
    184             self.assertHeader('Location', 'http://%s:%s%s/dir1/dir2/' 
    185                               % (self.HOST, self.PORT, prefix)) 
     184            self.assertHeader('Location', '%s://%s:%s%s/dir1/dir2/' 
     185                              % (self.scheme, self.HOST, self.PORT, prefix)) 
    186186             
    187187            # Test extra trailing slash (should be redirected if configured). 
    188188            self.getPage("/dir1/myMethod/") 
    189189            self.assertStatus((302, 303)) 
    190             self.assertHeader('Location', 'http://%s:%s%s/dir1/myMethod' 
    191                               % (self.HOST, self.PORT, prefix)) 
     190            self.assertHeader('Location', '%s://%s:%s%s/dir1/myMethod' 
     191                              % (self.scheme, self.HOST, self.PORT, prefix)) 
    192192             
    193193            # Test that default method must be exposed in order to match. 
     
    203203            self.getPage("/redirect") 
    204204            self.assertStatus('302 Found') 
    205             self.assertHeader('Location', 'http://%s:%s%s/dir1/' 
    206                               % (self.HOST, self.PORT, prefix)) 
     205            self.assertHeader('Location', '%s://%s:%s%s/dir1/' 
     206                              % (self.scheme, self.HOST, self.PORT, prefix)) 
    207207             
    208208            # Test that we can use URL's which aren't all valid Python identifiers 
     
    220220            self.assertBody(url) 
    221221            self.getPage("/dir1/dir2/tree_url") 
    222             self.assertBody("http://%s:%s%s/extra" % 
    223                             (self.HOST, self.PORT, prefix)) 
     222            self.assertBody("%s://%s:%s%s/extra" % 
     223                            (self.scheme, self.HOST, self.PORT, prefix)) 
    224224             
    225225            # Test that configs don't overwrite each other from diferent apps 
  • trunk/cherrypy/test/test_proxy.py

    r1339 r1345  
    4747        self.getPage("/") 
    4848        self.assertHeader('Location', 
    49                           "http://www.mydomain.com%s/dummy" % self.prefix()) 
     49                          "%s://www.mydomain.com%s/dummy" % (self.scheme, self.prefix())) 
    5050         
    5151        # Test X-Forwarded-Host (Apache 1.3.33+ and Apache 2) 
     
    5353        self.assertHeader('Location', "http://www.yetanother.com/dummy") 
    5454        self.getPage("/", headers=[('X-Forwarded-Host', 'www.yetanother.com')]) 
    55         self.assertHeader('Location', "http://www.yetanother.com/dummy"
     55        self.assertHeader('Location', "%s://www.yetanother.com/dummy" % self.scheme
    5656         
    5757        # Test X-Forwarded-For (Apache2) 
     
    6565        # Test X-Host (lighttpd; see https://trac.lighttpd.net/trac/ticket/418) 
    6666        self.getPage("/xhost", headers=[('X-Host', 'www.yetanother.com')]) 
    67         self.assertHeader('Location', "http://www.yetanother.com/blah"
     67        self.assertHeader('Location', "%s://www.yetanother.com/blah" % self.scheme
    6868         
    6969        # Test X-Forwarded-Proto (lighttpd) 
     
    7474        for sn in script_names: 
    7575            self.getPage(sn + "/newurl") 
    76             self.assertBody("Browse to <a href='http://www.mydomain.com" 
     76            self.assertBody("Browse to <a href='%s://www.mydomain.com" % self.scheme 
    7777                            + sn + "/this/new/page'>this page</a>.") 
    7878            self.getPage(sn + "/newurl", headers=[('X-Forwarded-Host', 
  • trunk/cherrypy/test/test_xmlrpc.py

    r1275 r1345  
    6262 
    6363 
     64class HTTPSTransport(xmlrpclib.SafeTransport): 
     65    """Subclass of SafeTransport to fix sock.recv errors (by using file).""" 
     66     
     67    def request(self, host, handler, request_body, verbose=0): 
     68        # issue XML-RPC request 
     69        h = self.make_connection(host) 
     70        if verbose: 
     71            h.set_debuglevel(1) 
     72         
     73        self.send_request(h, handler, request_body) 
     74        self.send_host(h, host) 
     75        self.send_user_agent(h) 
     76        self.send_content(h, request_body) 
     77         
     78        errcode, errmsg, headers = h.getreply() 
     79        if errcode != 200: 
     80            raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg, 
     81                                          headers) 
     82         
     83        self.verbose = verbose 
     84        return self.parse_response(h.getfile()) 
     85 
     86 
    6487from cherrypy.test import helper 
    6588 
     
    6891         
    6992        # load the appropriate xmlrpc proxy 
    70         url = 'http://localhost:%s/xmlrpc/' % (self.PORT) 
    71         proxy = xmlrpclib.ServerProxy(url) 
     93        if getattr(self.harness, "scheme", "http") == "https": 
     94            url = 'https://localhost:%s/xmlrpc/' % self.PORT 
     95            proxy = xmlrpclib.ServerProxy(url, transport=HTTPSTransport()) 
     96        else: 
     97            url = 'http://localhost:%s/xmlrpc/' % self.PORT 
     98            proxy = xmlrpclib.ServerProxy(url) 
    7299         
    73100        # begin the tests ... 

Hosted by WebFaction

Log in as guest/cpguest to create tickets