| 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:]) |