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