| | 1 | """ |
|---|
| | 2 | FastCGI 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 which support the FastCGI protocol; |
|---|
| | 8 | this module can be run stand-alone 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 |
|---|
| | 16 | |
|---|
| | 17 | __version__ = "0.1" |
|---|
| | 18 | __all__ = ["runfastcgi"] |
|---|
| | 19 | |
|---|
| | 20 | FASTCGI_HELP = r"""runfcgi: |
|---|
| | 21 | Run this project as a fastcgi application. To do this, the |
|---|
| | 22 | flup package from http://www.saddi.com/software/flup/ is |
|---|
| | 23 | required. |
|---|
| | 24 | |
|---|
| | 25 | Usage: |
|---|
| | 26 | django-admin.py runfcgi --setttings=yourproject.settings [fcgi settings] |
|---|
| | 27 | manage.py runfcgi [fcgi settings] |
|---|
| | 28 | |
|---|
| | 29 | Optional Fcgi settings: (setting=value) |
|---|
| | 30 | host=HOSTNAME hostname to listen on.. |
|---|
| | 31 | port=PORTNUM port to listen on. |
|---|
| | 32 | socket=FILE UNIX socket to listen on. |
|---|
| | 33 | method=IMPL prefork or threaded (default prefork) |
|---|
| | 34 | maxspare=NUMBER max number of spare processes to keep running. |
|---|
| | 35 | minspare=NUMBER min number of spare processes to prefork. |
|---|
| | 36 | maxchildren=NUMBER hard limit number of processes in prefork mode. |
|---|
| | 37 | daemonize=BOOL whether to detach from terminal. |
|---|
| | 38 | pidfile=FILE write the spawned process-id to this file. |
|---|
| | 39 | workdir=DIRECTORY change to this directory when daemonizing |
|---|
| | 40 | |
|---|
| | 41 | Examples: |
|---|
| | 42 | Run a "standard" fastcgi process on a file-descriptor |
|---|
| | 43 | (for webservers which spawn your processes for you) |
|---|
| | 44 | $ manage.py runfcgi method=threaded |
|---|
| | 45 | |
|---|
| | 46 | Run a fastcgi server on a TCP host/port |
|---|
| | 47 | $ manage.py runfcgi method=prefork host=127.0.0.1 port=8025 |
|---|
| | 48 | |
|---|
| | 49 | Run a fastcgi server on a UNIX domain socket (posix platforms only) |
|---|
| | 50 | $ manage.py runfcgi method=prefork socket=/tmp/fcgi.sock |
|---|
| | 51 | |
|---|
| | 52 | Run a fastCGI as a daemon and write the spawned PID in a file |
|---|
| | 53 | $ manage.py runfcgi socket=/tmp/fcgi.sock method=prefork \ |
|---|
| | 54 | daemonize=true pidfile=/var/run/django-fcgi.pid |
|---|
| | 55 | |
|---|
| | 56 | """ |
|---|
| | 57 | |
|---|
| | 58 | FASTCGI_OPTIONS = { |
|---|
| | 59 | 'host': None, |
|---|
| | 60 | 'port': None, |
|---|
| | 61 | 'socket': None, |
|---|
| | 62 | 'method': 'fork', |
|---|
| | 63 | 'daemonize': None, |
|---|
| | 64 | 'workdir': '/', |
|---|
| | 65 | 'pidfile': None, |
|---|
| | 66 | 'maxspare': 5, |
|---|
| | 67 | 'minspare': 2, |
|---|
| | 68 | 'maxchildren': 50, |
|---|
| | 69 | } |
|---|
| | 70 | |
|---|
| | 71 | def fastcgi_help(message = None): |
|---|
| | 72 | print FASTCGI_HELP |
|---|
| | 73 | if message: |
|---|
| | 74 | print message |
|---|
| | 75 | return False |
|---|
| | 76 | |
|---|
| | 77 | |
|---|
| | 78 | def runfastcgi(argset): |
|---|
| | 79 | options = FASTCGI_OPTIONS.copy() |
|---|
| | 80 | for x in argset: |
|---|
| | 81 | if "=" in x: |
|---|
| | 82 | k, v = x.split('=', 1) |
|---|
| | 83 | else: |
|---|
| | 84 | k,v = x, True |
|---|
| | 85 | options[k.lower()] = v |
|---|
| | 86 | |
|---|
| | 87 | if "help" in options: |
|---|
| | 88 | return fastcgi_help() |
|---|
| | 89 | |
|---|
| | 90 | try: |
|---|
| | 91 | import flup |
|---|
| | 92 | except ImportError, e: |
|---|
| | 93 | print >> sys.stderr, "ERROR: %s" % e |
|---|
| | 94 | print >> sys.stderr, " Unable to load the flup package. In order to run django" |
|---|
| | 95 | print >> sys.stderr, " as a FastCGI application, you will need to get flup from" |
|---|
| | 96 | print >> sys.stderr, " http://www.saddi.com/software/flup/ If you have flup already" |
|---|
| | 97 | print >> sys.stderr, " installed, then make sure you have it in your pythonpath." |
|---|
| | 98 | return False |
|---|
| | 99 | |
|---|
| | 100 | if options['method'] in ('prefork', 'fork'): |
|---|
| | 101 | from flup.server.fcgi_fork import WSGIServer |
|---|
| | 102 | wsgi_opts = dict( |
|---|
| | 103 | maxSpare = int(options["maxspare"]), |
|---|
| | 104 | minSpare = int(options["minspare"]), |
|---|
| | 105 | maxChildren = int(options["maxchildren"]), |
|---|
| | 106 | ) |
|---|
| | 107 | elif options['method'] in ('thread', 'threaded'): |
|---|
| | 108 | from flup.server.fcgi import WSGIServer |
|---|
| | 109 | wsgi_opts = {} |
|---|
| | 110 | else: |
|---|
| | 111 | return fastcgi_help("ERROR: implementation must be one of prefork or thread") |
|---|
| | 112 | |
|---|
| | 113 | # prep up and go |
|---|
| | 114 | from django.core.handlers.wsgi import WSGIHandler |
|---|
| | 115 | |
|---|
| | 116 | if options["host"] and options["port"] and not options["socket"]: |
|---|
| | 117 | wsgi_opts['bindAddress'] = (options["host"], int(options["port"])) |
|---|
| | 118 | elif options["socket"] and not options["host"] and not options["port"]: |
|---|
| | 119 | wsgi_opts['bindAddress'] = options["socket"] |
|---|
| | 120 | elif not options["socket"] and not options["host"] and not options["port"]: |
|---|
| | 121 | wsgi_opts['bindAddress'] = None |
|---|
| | 122 | else: |
|---|
| | 123 | return fastcgi_help("invalid combination of host,port,socket.") |
|---|
| | 124 | |
|---|
| | 125 | if options["daemonize"] is None: |
|---|
| | 126 | # default to daemonizing if we are running on a socket/named pipe |
|---|
| | 127 | daemonize = (wsgi_opts['bindAddress'] is not None) |
|---|
| | 128 | else: |
|---|
| | 129 | if options["daemonize"].lower() in ('true', 'yes', 't'): |
|---|
| | 130 | daemonize = True |
|---|
| | 131 | elif options["daemonize"].lower() in ('false', 'no', 'f'): |
|---|
| | 132 | daemonize = False |
|---|
| | 133 | else: |
|---|
| | 134 | return fastcgi_help("ERROR: invalid option for daemonize parameter") |
|---|
| | 135 | |
|---|
| | 136 | if daemonize: |
|---|
| | 137 | from django.utils.daemonize import become_daemon |
|---|
| | 138 | become_daemon(ourHomeDir=options["workdir"]) |
|---|
| | 139 | |
|---|
| | 140 | if options["pidfile"]: |
|---|
| | 141 | fp = open(options["pidfile"], "w") |
|---|
| | 142 | fp.write("%d\n" % os.getpid()) |
|---|
| | 143 | fp.close() |
|---|
| | 144 | |
|---|
| | 145 | WSGIServer(WSGIHandler(), **wsgi_opts).run() |
|---|
| | 146 | |
|---|
| | 147 | if __name__ == '__main__': |
|---|
| | 148 | runfastcgi(sys.argv[1:]) |