changeset 98:69b39aae73e7

Merge from flup-server.
author Allan Saddi <allan@saddi.com>
date Fri, 29 May 2009 10:43:28 -0700
parents fa0bc062585c 554d91b1ffe6
children 534a9665e3a6
files flup/server/ajp.py flup/server/ajp_fork.py flup/server/fcgi.py flup/server/fcgi_fork.py flup/server/fcgi_single.py flup/server/scgi.py flup/server/scgi_fork.py flup/server/threadedserver.py flup/server/threadpool.py
diffstat 13 files changed, 210 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/.hgtags	Fri May 15 20:09:51 2009 -0700
     1.2 +++ b/.hgtags	Fri May 29 10:43:28 2009 -0700
     1.3 @@ -1,2 +1,3 @@
     1.4  115fd353fe14c679554a0f1d11a3b80caa4a45b7 1.0.1
     1.5  6a1a1235936bb346f76b76c5ebb20c5c1067e19f 1.0
     1.6 +c6ea54fea75067cff10c8e86ce5077e0f31acb37 1.0.2
     2.1 --- a/ChangeLog	Fri May 15 20:09:51 2009 -0700
     2.2 +++ b/ChangeLog	Fri May 29 10:43:28 2009 -0700
     2.3 @@ -1,3 +1,20 @@
     2.4 +2009-05-29  Allan Saddi  <allan@saddi.com>
     2.5 +
     2.6 +	* Let all the active requests to finish before quitting. Thanks
     2.7 +	  to Anand Chitipothu for the patch!
     2.8 +
     2.9 +2009-05-26  Allan Saddi  <allan@saddi.com>
    2.10 +
    2.11 +	* Release 1.0.2
    2.12 +
    2.13 +2009-05-18  Allan Saddi  <allan@saddi.com>
    2.14 +
    2.15 +	* Import Paste factories (and dependencies...) from PasteScript
    2.16 +
    2.17 +2009-05-04  Allan Saddi  <allan@saddi.com>
    2.18 +
    2.19 +	* Be tolerant of EAGAIN when sending messages to parent process.
    2.20 +
    2.21  2009-02-02  Allan Saddi  <allan@saddi.com>
    2.22  
    2.23  	* Add forceCGI keyword argument to FastCGI servers to
     3.1 --- a/flup/server/ajp.py	Fri May 15 20:09:51 2009 -0700
     3.2 +++ b/flup/server/ajp.py	Fri May 29 10:43:28 2009 -0700
     3.3 @@ -156,16 +156,16 @@
     3.4          ret = ThreadedServer.run(self, sock)
     3.5  
     3.6          self._cleanupSocket(sock)
     3.7 +        # AJP connections are more or less persistent. .shutdown() will
     3.8 +        # not return until the web server lets go. So don't bother calling
     3.9 +        # it...
    3.10 +        #self.shutdown()
    3.11  
    3.12          self.logger.info('%s shutting down%s', self.__class__.__name__,
    3.13                           self._hupReceived and ' (reload requested)' or '')
    3.14  
    3.15          return ret
    3.16  
    3.17 -def factory(global_conf, host=None, port=None, **local):
    3.18 -    from . import paste_factory
    3.19 -    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
    3.20 -
    3.21  if __name__ == '__main__':
    3.22      def test_app(environ, start_response):
    3.23          """Probably not the most efficient example."""
     4.1 --- a/flup/server/ajp_fork.py	Fri May 15 20:09:51 2009 -0700
     4.2 +++ b/flup/server/ajp_fork.py	Fri May 29 10:43:28 2009 -0700
     4.3 @@ -160,10 +160,6 @@
     4.4  
     4.5          return ret
     4.6  
     4.7 -def factory(global_conf, host=None, port=None, **local):
     4.8 -    from . import paste_factory
     4.9 -    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
    4.10 -
    4.11  if __name__ == '__main__':
    4.12      def test_app(environ, start_response):
    4.13          """Probably not the most efficient example."""
     5.1 --- a/flup/server/fcgi.py	Fri May 15 20:09:51 2009 -0700
     5.2 +++ b/flup/server/fcgi.py	Fri May 29 10:43:28 2009 -0700
     5.3 @@ -113,13 +113,10 @@
     5.4          ret = ThreadedServer.run(self, sock)
     5.5  
     5.6          self._cleanupSocket(sock)
     5.7 +        self.shutdown()
     5.8  
     5.9          return ret
    5.10  
    5.11 -def factory(global_conf, host=None, port=None, **local):
    5.12 -    from . import paste_factory
    5.13 -    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
    5.14 -
    5.15  if __name__ == '__main__':
    5.16      def test_app(environ, start_response):
    5.17          """Probably not the most efficient example."""
     6.1 --- a/flup/server/fcgi_fork.py	Fri May 15 20:09:51 2009 -0700
     6.2 +++ b/flup/server/fcgi_fork.py	Fri May 29 10:43:28 2009 -0700
     6.3 @@ -135,10 +135,6 @@
     6.4  
     6.5          return ret
     6.6  
     6.7 -def factory(global_conf, host=None, port=None, **local):
     6.8 -    from . import paste_factory
     6.9 -    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
    6.10 -
    6.11  if __name__ == '__main__':
    6.12      def test_app(environ, start_response):
    6.13          """Probably not the most efficient example."""
     7.1 --- a/flup/server/fcgi_single.py	Fri May 15 20:09:51 2009 -0700
     7.2 +++ b/flup/server/fcgi_single.py	Fri May 29 10:43:28 2009 -0700
     7.3 @@ -121,10 +121,6 @@
     7.4  
     7.5          return ret
     7.6  
     7.7 -def factory(global_conf, host=None, port=None, **local):
     7.8 -    from . import paste_factory
     7.9 -    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
    7.10 -
    7.11  if __name__ == '__main__':
    7.12      def test_app(environ, start_response):
    7.13          """Probably not the most efficient example."""
     8.1 --- a/flup/server/paste_factory.py	Fri May 15 20:09:51 2009 -0700
     8.2 +++ b/flup/server/paste_factory.py	Fri May 29 10:43:28 2009 -0700
     8.3 @@ -1,18 +1,121 @@
     8.4 -def helper(wsgiServerClass, global_conf, host, port, **local_conf):
     8.5 -    # I think I can't write a tuple for bindAddress in .ini file
     8.6 -    host = host or global_conf.get('host', 'localhost')
     8.7 -    port = port or global_conf.get('port', 4000)
     8.8 +# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
     8.9 +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
    8.10 +def asbool(obj):
    8.11 +    if isinstance(obj, (str, unicode)):
    8.12 +        obj = obj.strip().lower()
    8.13 +        if obj in ['true', 'yes', 'on', 'y', 't', '1']:
    8.14 +            return True
    8.15 +        elif obj in ['false', 'no', 'off', 'n', 'f', '0']:
    8.16 +            return False
    8.17 +        else:
    8.18 +            raise ValueError(
    8.19 +                "String is not true/false: %r" % obj)
    8.20 +    return bool(obj)
    8.21  
    8.22 -    if 'socket' in local_conf:
    8.23 -        local_conf['bindAddress'] = local_conf['socket']
    8.24 -        del local_conf['socket']
    8.25 -        if 'umask' in local_conf:
    8.26 -            local_conf['umask'] = int(local_conf['umask'], 8)
    8.27 +def aslist(obj, sep=None, strip=True):
    8.28 +    if isinstance(obj, (str, unicode)):
    8.29 +        lst = obj.split(sep)
    8.30 +        if strip:
    8.31 +            lst = [v.strip() for v in lst]
    8.32 +        return lst
    8.33 +    elif isinstance(obj, (list, tuple)):
    8.34 +        return obj
    8.35 +    elif obj is None:
    8.36 +        return []
    8.37      else:
    8.38 -        local_conf['bindAddress'] = (host, int(port))
    8.39 +        return [obj]
    8.40 +
    8.41 +def run_ajp_thread(wsgi_app, global_conf,
    8.42 +                   scriptName='', host='localhost', port='8009',
    8.43 +                   allowedServers='127.0.0.1'):
    8.44 +    import flup.server.ajp
    8.45 +    addr = (host, int(port))
    8.46 +    s = flup.server.ajp.WSGIServer(
    8.47 +        wsgi_app,
    8.48 +        scriptName=scriptName,
    8.49 +        bindAddress=addr,
    8.50 +        allowedServers=aslist(allowedServers),
    8.51 +        )
    8.52 +    s.run()
    8.53      
    8.54 -    def server(application):
    8.55 -        server = wsgiServerClass(application, **local_conf)
    8.56 -        server.run()
    8.57 +def run_ajp_fork(wsgi_app, global_conf,
    8.58 +                 scriptName='', host='localhost', port='8009',
    8.59 +                 allowedServers='127.0.0.1'):
    8.60 +    import flup.server.ajp_fork
    8.61 +    addr = (host, int(port))
    8.62 +    s = flup.server.ajp_fork.WSGIServer(
    8.63 +        wsgi_app,
    8.64 +        scriptName=scriptName,
    8.65 +        bindAddress=addr,
    8.66 +        allowedServers=aslist(allowedServers),
    8.67 +        )
    8.68 +    s.run()
    8.69  
    8.70 -    return server
    8.71 +def run_fcgi_thread(wsgi_app, global_conf,
    8.72 +                    host=None, port=None,
    8.73 +                    socket=None, umask=None,
    8.74 +                    multiplexed=False):
    8.75 +    import flup.server.fcgi
    8.76 +    if socket:
    8.77 +        assert host is None and port is None
    8.78 +        sock = socket
    8.79 +    elif host:
    8.80 +        assert host is not None and port is not None
    8.81 +        sock = (host, int(port))
    8.82 +    else:
    8.83 +        sock = None
    8.84 +    if umask is not None:
    8.85 +        umask = int(umask)
    8.86 +    s = flup.server.fcgi.WSGIServer(
    8.87 +        wsgi_app,
    8.88 +        bindAddress=sock, umask=umask,
    8.89 +        multiplexed=asbool(multiplexed))
    8.90 +    s.run()
    8.91 +
    8.92 +def run_fcgi_fork(wsgi_app, global_conf,
    8.93 +                  host=None, port=None,
    8.94 +                  socket=None, umask=None,
    8.95 +                  multiplexed=False):
    8.96 +    import flup.server.fcgi_fork
    8.97 +    if socket:
    8.98 +        assert host is None and port is None
    8.99 +        sock = socket
   8.100 +    elif host:
   8.101 +        assert host is not None and port is not None
   8.102 +        sock = (host, int(port))
   8.103 +    else:
   8.104 +        sock = None
   8.105 +    if umask is not None:
   8.106 +        umask = int(umask)
   8.107 +    s = flup.server.fcgi_fork.WSGIServer(
   8.108 +        wsgi_app,
   8.109 +        bindAddress=sock, umask=umask,
   8.110 +        multiplexed=asbool(multiplexed))
   8.111 +    s.run()
   8.112 +
   8.113 +def run_scgi_thread(wsgi_app, global_conf,
   8.114 +                    scriptName='', host='localhost', port='4000',
   8.115 +                    allowedServers='127.0.0.1'):
   8.116 +    import flup.server.scgi
   8.117 +    addr = (host, int(port))
   8.118 +    s = flup.server.scgi.WSGIServer(
   8.119 +        wsgi_app,
   8.120 +        scriptName=scriptName,
   8.121 +        bindAddress=addr,
   8.122 +        allowedServers=aslist(allowedServers),
   8.123 +        )
   8.124 +    s.run()
   8.125 +
   8.126 +def run_scgi_fork(wsgi_app, global_conf,
   8.127 +                  scriptName='', host='localhost', port='4000',
   8.128 +                  allowedServers='127.0.0.1'):
   8.129 +    import flup.server.scgi_fork
   8.130 +    addr = (host, int(port))
   8.131 +    s = flup.server.scgi_fork.WSGIServer(
   8.132 +        wsgi_app,
   8.133 +        scriptName=scriptName,
   8.134 +        bindAddress=addr,
   8.135 +        allowedServers=aslist(allowedServers),
   8.136 +        )
   8.137 +    s.run()
   8.138 +    
     9.1 --- a/flup/server/scgi.py	Fri May 15 20:09:51 2009 -0700
     9.2 +++ b/flup/server/scgi.py	Fri May 29 10:43:28 2009 -0700
     9.3 @@ -151,16 +151,13 @@
     9.4          ret = ThreadedServer.run(self, sock)
     9.5  
     9.6          self._cleanupSocket(sock)
     9.7 +        self.shutdown()
     9.8  
     9.9          self.logger.info('%s shutting down%s', self.__class__.__name__,
    9.10                           self._hupReceived and ' (reload requested)' or '')
    9.11  
    9.12          return ret
    9.13  
    9.14 -def factory(global_conf, host=None, port=None, **local):
    9.15 -    from . import paste_factory
    9.16 -    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
    9.17 -
    9.18  if __name__ == '__main__':
    9.19      def test_app(environ, start_response):
    9.20          """Probably not the most efficient example."""
    10.1 --- a/flup/server/scgi_fork.py	Fri May 15 20:09:51 2009 -0700
    10.2 +++ b/flup/server/scgi_fork.py	Fri May 29 10:43:28 2009 -0700
    10.3 @@ -155,10 +155,6 @@
    10.4  
    10.5          return ret
    10.6  
    10.7 -def factory(global_conf, host=None, port=None, **local):
    10.8 -    from . import paste_factory
    10.9 -    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
   10.10 -
   10.11  if __name__ == '__main__':
   10.12      def test_app(environ, start_response):
   10.13          """Probably not the most efficient example."""
    11.1 --- a/flup/server/threadedserver.py	Fri May 15 20:09:51 2009 -0700
    11.2 +++ b/flup/server/threadedserver.py	Fri May 29 10:43:28 2009 -0700
    11.3 @@ -109,6 +109,10 @@
    11.4  
    11.5          # Return bool based on whether or not SIGHUP was received.
    11.6          return self._hupReceived
    11.7 +        
    11.8 +    def shutdown(self):
    11.9 +        """Wait for running threads to finish."""
   11.10 +        self._threadPool.shutdown()
   11.11  
   11.12      def _mainloopPeriodic(self):
   11.13          """
    12.1 --- a/flup/server/threadpool.py	Fri May 15 20:09:51 2009 -0700
    12.2 +++ b/flup/server/threadpool.py	Fri May 29 10:43:28 2009 -0700
    12.3 @@ -28,7 +28,6 @@
    12.4  __version__ = '$Revision$'
    12.5  
    12.6  import sys
    12.7 -import _thread
    12.8  import threading
    12.9  
   12.10  class ThreadPool(object):
   12.11 @@ -47,9 +46,30 @@
   12.12          self._workQueue = []
   12.13          self._idleCount = self._workerCount = maxSpare
   12.14  
   12.15 +        self._threads = []
   12.16 +        self._stop = False
   12.17 +
   12.18          # Start the minimum number of worker threads.
   12.19          for i in range(maxSpare):
   12.20 -            _thread.start_new_thread(self._worker, ())
   12.21 +            self._start_new_thread()
   12.22 +            
   12.23 +    def _start_new_thread(self):
   12.24 +        t = threading.Thread(target=self._worker)
   12.25 +        self._threads.append(t)
   12.26 +        t.daemon = True
   12.27 +        t.start()
   12.28 +        return t
   12.29 +        
   12.30 +    def shutdown(self):
   12.31 +        """shutdown all workers."""
   12.32 +        self._lock.acquire()
   12.33 +        self._stop = True
   12.34 +        self._lock.notifyAll()
   12.35 +        self._lock.release()
   12.36 +
   12.37 +        # wait for all threads to finish
   12.38 +        for t in self._threads:
   12.39 +            t.join()
   12.40  
   12.41      def addJob(self, job, allowQueuing=True):
   12.42          """
   12.43 @@ -71,7 +91,7 @@
   12.44                    self._workerCount < self._maxThreads:
   12.45                  self._workerCount += 1
   12.46                  self._idleCount += 1
   12.47 -                _thread.start_new_thread(self._worker, ())
   12.48 +                self._start_new_thread()
   12.49  
   12.50              # Hand off the job.
   12.51              if self._idleCount or allowQueuing:
   12.52 @@ -88,34 +108,38 @@
   12.53          Worker thread routine. Waits for a job, executes it, repeat.
   12.54          """
   12.55          self._lock.acquire()
   12.56 -        while True:
   12.57 -            while not self._workQueue:
   12.58 -                self._lock.wait()
   12.59 +        try:
   12.60 +            while True:
   12.61 +                while not self._workQueue and not self._stop:
   12.62 +                    self._lock.wait()
   12.63 +                
   12.64 +                if self._stop:
   12.65 +                    return
   12.66  
   12.67 -            # We have a job to do...
   12.68 -            job = self._workQueue.pop(0)
   12.69 +                # We have a job to do...
   12.70 +                job = self._workQueue.pop(0)
   12.71  
   12.72 -            assert self._idleCount > 0
   12.73 -            self._idleCount -= 1
   12.74 +                assert self._idleCount > 0
   12.75 +                self._idleCount -= 1
   12.76  
   12.77 +                self._lock.release()
   12.78 +
   12.79 +                try:
   12.80 +                    job.run()
   12.81 +                except:
   12.82 +                    # FIXME: This should really be reported somewhere.
   12.83 +                    # But we can't simply report it to stderr because of fcgi
   12.84 +                    pass
   12.85 +
   12.86 +                self._lock.acquire()
   12.87 +
   12.88 +                if self._idleCount == self._maxSpare:
   12.89 +                    break # NB: lock still held
   12.90 +                self._idleCount += 1
   12.91 +                assert self._idleCount <= self._maxSpare
   12.92 +
   12.93 +            # Die off...
   12.94 +            assert self._workerCount > self._maxSpare
   12.95 +            self._workerCount -= 1
   12.96 +        finally:
   12.97              self._lock.release()
   12.98 -
   12.99 -            try:
  12.100 -                job.run()
  12.101 -            except:
  12.102 -                # FIXME: This should really be reported somewhere.
  12.103 -                # But we can't simply report it to stderr because of fcgi
  12.104 -                pass
  12.105 -
  12.106 -            self._lock.acquire()
  12.107 -
  12.108 -            if self._idleCount == self._maxSpare:
  12.109 -                break # NB: lock still held
  12.110 -            self._idleCount += 1
  12.111 -            assert self._idleCount <= self._maxSpare
  12.112 -
  12.113 -        # Die off...
  12.114 -        assert self._workerCount > self._maxSpare
  12.115 -        self._workerCount -= 1
  12.116 -
  12.117 -        self._lock.release()
    13.1 --- a/setup.py	Fri May 15 20:09:51 2009 -0700
    13.2 +++ b/setup.py	Fri May 29 10:43:28 2009 -0700
    13.3 @@ -5,21 +5,21 @@
    13.4  from setuptools import setup, find_packages
    13.5  setup(
    13.6      name = 'flup',
    13.7 -    version = '1.0.1',
    13.8 +    version = '1.0.2',
    13.9      packages = find_packages(),
   13.10      zip_safe = True,
   13.11      
   13.12      entry_points = """
   13.13 -    [paste.server_factory]
   13.14 -    ajp = flup.server.ajp:factory
   13.15 -    fcgi = flup.server.fcgi:factory
   13.16 -    scgi = flup.server.scgi:factory
   13.17 -    ajp_thread = flup.server.ajp:factory
   13.18 -    fcgi_thread = flup.server.fcgi:factory
   13.19 -    scgi_thread = flup.server.scgi:factory
   13.20 -    ajp_fork = flup.server.ajp_fork:factory
   13.21 -    fcgi_fork = flup.server.fcgi_fork:factory
   13.22 -    scgi_fork = flup.server.scgi_fork:factory
   13.23 +    [paste.server_runner]
   13.24 +    ajp = flup.server.paste_factory:run_ajp_thread
   13.25 +    fcgi = flup.server.paste_factory:run_fcgi_thread
   13.26 +    scgi = flup.server.paste_factory:run_scgi_thread
   13.27 +    ajp_thread = flup.server.paste_factory:run_ajp_thread
   13.28 +    fcgi_thread = flup.server.paste_factory:run_fcgi_thread
   13.29 +    scgi_thread = flup.server.paste_factory:run_scgi_thread
   13.30 +    ajp_fork = flup.server.paste_factory:run_ajp_fork
   13.31 +    fcgi_fork = flup.server.paste_factory:run_fcgi_fork
   13.32 +    scgi_fork = flup.server.paste_factory:run_scgi_fork
   13.33      """,
   13.34      
   13.35      author = 'Allan Saddi',