app/django/core/servers/fastcgi.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 """
       
     2 FastCGI (or SCGI, or AJP1.3 ...) server that implements the WSGI protocol.
       
     3 
       
     4 Uses the flup python package: http://www.saddi.com/software/flup/
       
     5 
       
     6 This is a adaptation of the flup package to add FastCGI server support
       
     7 to run Django apps from Web servers that support the FastCGI protocol.
       
     8 This module can be run standalone or from the django-admin / manage.py
       
     9 scripts using the "runfcgi" directive.
       
    10 
       
    11 Run with the extra option "help" for a list of additional options you can
       
    12 pass to this server.
       
    13 """
       
    14 
       
    15 import sys, os
       
    16 
       
    17 __version__ = "0.1"
       
    18 __all__ = ["runfastcgi"]
       
    19 
       
    20 FASTCGI_HELP = r"""
       
    21   Run this project as a fastcgi (or some other protocol supported
       
    22   by flup) application. To do this, the flup package from
       
    23   http://www.saddi.com/software/flup/ is required.
       
    24 
       
    25    runfcgi [options] [fcgi settings]
       
    26 
       
    27 Optional Fcgi settings: (setting=value)
       
    28   protocol=PROTOCOL    fcgi, scgi, ajp, ... (default fcgi)
       
    29   host=HOSTNAME        hostname to listen on..
       
    30   port=PORTNUM         port to listen on.
       
    31   socket=FILE          UNIX socket to listen on.
       
    32   method=IMPL          prefork or threaded (default prefork)
       
    33   maxrequests=NUMBER   number of requests a child handles before it is 
       
    34                        killed and a new child is forked (0 = no limit).
       
    35   maxspare=NUMBER      max number of spare processes / threads
       
    36   minspare=NUMBER      min number of spare processes / threads.
       
    37   maxchildren=NUMBER   hard limit number of processes / threads
       
    38   daemonize=BOOL       whether to detach from terminal.
       
    39   pidfile=FILE         write the spawned process-id to this file.
       
    40   workdir=DIRECTORY    change to this directory when daemonizing.
       
    41   outlog=FILE          write stdout to this file.
       
    42   errlog=FILE          write stderr to this file.
       
    43 
       
    44 Examples:
       
    45   Run a "standard" fastcgi process on a file-descriptor
       
    46   (for webservers which spawn your processes for you)
       
    47     $ manage.py runfcgi method=threaded
       
    48 
       
    49   Run a scgi server on a TCP host/port
       
    50     $ manage.py runfcgi protocol=scgi method=prefork host=127.0.0.1 port=8025
       
    51 
       
    52   Run a fastcgi server on a UNIX domain socket (posix platforms only)
       
    53     $ manage.py runfcgi method=prefork socket=/tmp/fcgi.sock
       
    54 
       
    55   Run a fastCGI as a daemon and write the spawned PID in a file
       
    56     $ manage.py runfcgi socket=/tmp/fcgi.sock method=prefork \
       
    57         daemonize=true pidfile=/var/run/django-fcgi.pid
       
    58 
       
    59 """
       
    60 
       
    61 FASTCGI_OPTIONS = {
       
    62     'protocol': 'fcgi',
       
    63     'host': None,
       
    64     'port': None,
       
    65     'socket': None,
       
    66     'method': 'fork',
       
    67     'daemonize': None,
       
    68     'workdir': '/',
       
    69     'pidfile': None,
       
    70     'maxspare': 5,
       
    71     'minspare': 2,
       
    72     'maxchildren': 50,
       
    73     'maxrequests': 0,
       
    74     'outlog': None,
       
    75     'errlog': None,
       
    76 }
       
    77 
       
    78 def fastcgi_help(message=None):
       
    79     print FASTCGI_HELP
       
    80     if message:
       
    81         print message
       
    82     return False
       
    83 
       
    84 def runfastcgi(argset=[], **kwargs):
       
    85     options = FASTCGI_OPTIONS.copy()
       
    86     options.update(kwargs)
       
    87     for x in argset:
       
    88         if "=" in x:
       
    89             k, v = x.split('=', 1)
       
    90         else:
       
    91             k, v = x, True
       
    92         options[k.lower()] = v
       
    93 
       
    94     if "help" in options:
       
    95         return fastcgi_help()
       
    96 
       
    97     try:
       
    98         import flup
       
    99     except ImportError, e:
       
   100         print >> sys.stderr, "ERROR: %s" % e
       
   101         print >> sys.stderr, "  Unable to load the flup package.  In order to run django"
       
   102         print >> sys.stderr, "  as a FastCGI application, you will need to get flup from"
       
   103         print >> sys.stderr, "  http://www.saddi.com/software/flup/   If you've already"
       
   104         print >> sys.stderr, "  installed flup, then make sure you have it in your PYTHONPATH."
       
   105         return False
       
   106 
       
   107     flup_module = 'server.' + options['protocol']
       
   108 
       
   109     if options['method'] in ('prefork', 'fork'):
       
   110         wsgi_opts = {
       
   111             'maxSpare': int(options["maxspare"]),
       
   112             'minSpare': int(options["minspare"]),
       
   113             'maxChildren': int(options["maxchildren"]),
       
   114             'maxRequests': int(options["maxrequests"]), 
       
   115         }
       
   116         flup_module += '_fork'
       
   117     elif options['method'] in ('thread', 'threaded'):
       
   118         wsgi_opts = {
       
   119             'maxSpare': int(options["maxspare"]),
       
   120             'minSpare': int(options["minspare"]),
       
   121             'maxThreads': int(options["maxchildren"]),
       
   122         }
       
   123     else:
       
   124         return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
       
   125 
       
   126     wsgi_opts['debug'] = False # Turn off flup tracebacks
       
   127 
       
   128     try:
       
   129         WSGIServer = getattr(__import__('flup.' + flup_module, '', '', flup_module), 'WSGIServer')
       
   130     except:
       
   131         print "Can't import flup." + flup_module
       
   132         return False
       
   133 
       
   134     # Prep up and go
       
   135     from django.core.handlers.wsgi import WSGIHandler
       
   136 
       
   137     if options["host"] and options["port"] and not options["socket"]:
       
   138         wsgi_opts['bindAddress'] = (options["host"], int(options["port"]))
       
   139     elif options["socket"] and not options["host"] and not options["port"]:
       
   140         wsgi_opts['bindAddress'] = options["socket"]
       
   141     elif not options["socket"] and not options["host"] and not options["port"]:
       
   142         wsgi_opts['bindAddress'] = None
       
   143     else:
       
   144         return fastcgi_help("Invalid combination of host, port, socket.")
       
   145 
       
   146     if options["daemonize"] is None:
       
   147         # Default to daemonizing if we're running on a socket/named pipe.
       
   148         daemonize = (wsgi_opts['bindAddress'] is not None)
       
   149     else:
       
   150         if options["daemonize"].lower() in ('true', 'yes', 't'):
       
   151             daemonize = True
       
   152         elif options["daemonize"].lower() in ('false', 'no', 'f'):
       
   153             daemonize = False
       
   154         else:
       
   155             return fastcgi_help("ERROR: Invalid option for daemonize parameter.")
       
   156 
       
   157     daemon_kwargs = {}
       
   158     if options['outlog']:
       
   159         daemon_kwargs['out_log'] = options['outlog']
       
   160     if options['errlog']:
       
   161         daemon_kwargs['err_log'] = options['errlog']
       
   162 
       
   163     if daemonize:
       
   164         from django.utils.daemonize import become_daemon
       
   165         become_daemon(our_home_dir=options["workdir"], **daemon_kwargs)
       
   166 
       
   167     if options["pidfile"]:
       
   168         fp = open(options["pidfile"], "w")
       
   169         fp.write("%d\n" % os.getpid())
       
   170         fp.close()
       
   171 
       
   172     WSGIServer(WSGIHandler(), **wsgi_opts).run()
       
   173 
       
   174 if __name__ == '__main__':
       
   175     runfastcgi(sys.argv[1:])