Code

Ticket #1736: django-fastcgi-new-fixed.diff

File django-fastcgi-new-fixed.diff, 8.3 KB (added by jcrasta@…, 8 years ago)

fixed a spelling error, and added an import

Line 
1Index: django/core/servers/fastcgi.py
2===================================================================
3--- django/core/servers/fastcgi.py      (revision 0)
4+++ django/core/servers/fastcgi.py      (revision 0)
5@@ -0,0 +1,148 @@
6+"""
7+FastCGI server that implements the WSGI protocol.
8+
9+Uses the flup python package: http://www.saddi.com/software/flup/
10+
11+This is a adaptation of the flup package to add FastCGI server support
12+to run Django apps from web-servers which support the FastCGI protocol;
13+this module can be run stand-alone or from the django-admin / manage.py
14+scripts using the `runfcgi` directive.
15+
16+Run with the extra option "help" for a list of additional options you can
17+pass to this server.
18+"""
19+
20+import sys, os
21+
22+__version__ = "0.1"
23+__all__ = ["runfastcgi"]
24+
25+FASTCGI_HELP = r"""runfcgi:
26+  Run this project as a fastcgi application. To do this, the
27+  flup package from http://www.saddi.com/software/flup/ is
28+  required.
29+
30+Usage:
31+   django-admin.py runfcgi --settings=yourproject.settings [fcgi settings]
32+   manage.py runfcgi [fcgi settings]
33+     
34+Optional Fcgi settings: (setting=value)
35+  host=HOSTNAME        hostname to listen on..
36+  port=PORTNUM         port to listen on.
37+  socket=FILE          UNIX socket to listen on.
38+  method=IMPL          prefork or threaded (default prefork)
39+  maxspare=NUMBER      max number of spare processes to keep running.
40+  minspare=NUMBER      min number of spare processes to prefork.
41+  maxchildren=NUMBER   hard limit number of processes in prefork mode.
42+  daemonize=BOOL       whether to detach from terminal.
43+  pidfile=FILE         write the spawned process-id to this file.
44+  workdir=DIRECTORY    change to this directory when daemonizing
45+
46+Examples:
47+  Run a "standard" fastcgi process on a file-descriptor
48+  (for webservers which spawn your processes for you)
49+    $ manage.py runfcgi method=threaded
50+
51+  Run a fastcgi server on a TCP host/port
52+    $ manage.py runfcgi method=prefork host=127.0.0.1 port=8025
53+
54+  Run a fastcgi server on a UNIX domain socket (posix platforms only)
55+    $ manage.py runfcgi method=prefork socket=/tmp/fcgi.sock
56+
57+  Run a fastCGI as a daemon and write the spawned PID in a file
58+    $ manage.py runfcgi socket=/tmp/fcgi.sock method=prefork \
59+        daemonize=true pidfile=/var/run/django-fcgi.pid
60
61+"""
62+
63+FASTCGI_OPTIONS = {
64+    'host': None,
65+    'port': None,
66+    'socket': None,
67+    'method': 'fork',
68+    'daemonize': None,
69+    'workdir': '/',
70+    'pidfile': None,
71+    'maxspare': 5,
72+    'minspare': 2,
73+    'maxchildren': 50,
74+}
75+
76+def fastcgi_help(message = None):
77+    print FASTCGI_HELP
78+    if message:
79+        print message
80+    return False
81+
82+
83+def runfastcgi(argset):
84+    options = FASTCGI_OPTIONS.copy()
85+    for x in argset:
86+        if "=" in x:
87+            k, v = x.split('=', 1)
88+        else:
89+            k,v = x, True
90+        options[k.lower()] = v
91+
92+    if "help" in options:
93+        return fastcgi_help()
94+
95+    try:
96+        import flup
97+    except ImportError, e:
98+        print >> sys.stderr, "ERROR: %s" % e
99+        print >> sys.stderr, "  Unable to load the flup package.  In order to run django"
100+        print >> sys.stderr, "  as a FastCGI application, you will need to get flup from"
101+        print >> sys.stderr, "  http://www.saddi.com/software/flup/   If you have flup already"
102+        print >> sys.stderr, "  installed, then make sure you have it in your PYTHONPATH."
103+        return False
104+       
105+    if options['method'] in ('prefork', 'fork'):
106+        from flup.server.fcgi_fork import WSGIServer
107+        wsgi_opts = dict(
108+            maxSpare = int(options["maxspare"]),
109+            minSpare = int(options["minspare"]),
110+            maxChildren = int(options["maxchildren"]),
111+        )
112+    elif options['method'] in ('thread', 'threaded'):
113+        from flup.server.fcgi import WSGIServer
114+        wsgi_opts = {}
115+    else:
116+        return fastcgi_help("ERROR: implementation must be one of prefork or thread")
117+
118+    # prep up and go
119+    from django.core.handlers.wsgi import WSGIHandler
120+
121+    if options["host"] and options["port"] and not options["socket"]:
122+        wsgi_opts['bindAddress'] = (options["host"], int(options["port"]))
123+    elif options["socket"] and not options["host"] and not options["port"]:
124+        wsgi_opts['bindAddress'] = options["socket"]
125+    elif not options["socket"] and not options["host"] and not options["port"]:
126+        wsgi_opts['bindAddress'] = None
127+    else:
128+        return fastcgi_help("invalid combination of host,port,socket.")
129+
130+    if options["daemonize"] is None:
131+        # default to daemonizing if we are running on a socket/named pipe
132+        daemonize = (wsgi_opts['bindAddress'] is not None)
133+    else:
134+        if options["daemonize"].lower() in ('true', 'yes', 't'):
135+            daemonize = True
136+        elif options["daemonize"].lower() in ('false', 'no', 'f'):
137+            daemonize = False
138+        else:
139+            return fastcgi_help("ERROR: invalid option for daemonize parameter")
140+   
141+    if daemonize:
142+        from django.utils.daemonize import become_daemon
143+        become_daemon(ourHomeDir=options["workdir"])
144+
145+    if options["pidfile"]:
146+        fp = open(options["pidfile"], "w")
147+        fp.write("%d\n" % os.getpid())
148+        fp.close()
149+
150+    WSGIServer(WSGIHandler(), **wsgi_opts).run()
151+
152+if __name__ == '__main__':
153+    runfastcgi(sys.argv[1:])
154Index: django/core/management.py
155===================================================================
156--- django/core/management.py   (revision 3147)
157+++ django/core/management.py   (working copy)
158@@ -1081,6 +1081,12 @@
159     runshell()
160 dbshell.args = ""
161 
162+def runfcgi(args):
163+    """Run this project as a FastCGI application. requires flup."""
164+    from django.core.servers.fastcgi import runfastcgi
165+    runfastcgi(args)
166+runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]'
167+
168 # Utilities for command-line script
169 
170 DEFAULT_ACTION_MAPPING = {
171@@ -1091,6 +1097,7 @@
172     'inspectdb': inspectdb,
173     'install': install,
174     'reset': reset,
175+    'runfcgi': runfcgi,
176     'runserver': runserver,
177     'shell': run_shell,
178     'sql': get_sql_create,
179@@ -1210,6 +1217,8 @@
180             except ValueError:
181                 addr, port = '', args[1]
182         action_mapping[action](addr, port)
183+    elif action == 'runfcgi':
184+        action_mapping[action](args[1:])
185     else:
186         from django.db import models
187         try:
188Index: django/utils/daemonize.py
189===================================================================
190--- django/utils/daemonize.py   (revision 0)
191+++ django/utils/daemonize.py   (revision 0)
192@@ -0,0 +1,71 @@
193+import os
194+import sys
195+
196+if os.name == 'posix':
197+
198+       def become_daemon(ourHomeDir='.',outLog='/dev/null',errLog='/dev/null'):
199+               """
200+               Robustly turn us into a UNIX daemon, running in ourHomeDir.
201+               Modelled after the original code of this module and some
202+               sample code from the net.
203+               """
204+
205+               # first fork
206+               try:
207+                       if os.fork() > 0:
208+                               sys.exit(0)     # kill off parent
209+               except OSError, e:
210+                       sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
211+                       sys.exit(1)
212+               os.setsid()
213+               os.chdir(ourHomeDir)
214+               os.umask(0)
215+               # second fork
216+               try:
217+                       if os.fork() > 0:
218+                               sys.exit(0)
219+               except OSError, e:
220+                       sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
221+                       sys.exit(1)
222+
223+               si = open('/dev/null', 'r')
224+               so = open(outLog, 'a+', 0)
225+               se = open(errLog, 'a+', 0)
226+               os.dup2(si.fileno(), sys.stdin.fileno())
227+               os.dup2(so.fileno(), sys.stdout.fileno())
228+               os.dup2(se.fileno(), sys.stderr.fileno())
229+
230+else:
231+
232+       def become_daemon(ourHomeDir='.',outLog=None,errLog=None):
233+               """
234+               If we are not running under a POSIX system, just simulate
235+               the daemon mode by doing redirections and directory changeing
236+               """
237+
238+               os.chdir(ourHomeDir)
239+               os.umask(0)
240+               sys.stdin.close()
241+               sys.stdout.close()
242+               sys.stderr.close()
243+               if errLog and outLog:
244+                       sys.stderr=open (errLog, 'a', 0)
245+                       sys.stdout=open (outLog, 'a', 0)
246+               elif errLog:
247+                       sys.stderr=open (errLog, 'a', 0)
248+                       sys.stdout=NullDevice ()
249+               elif outLog:
250+                       sys.stdout=open (outLog, 'a', 0)
251+                       sys.stderr=NullDevice ()
252+               else:
253+                       sys.stdout = NullDevice()
254+                       sys.stderr = NullDevice()
255+
256+       class NullDevice:
257+               """
258+               A substitute for stdout and stderr that writes to nowhere.
259+               This is a substitute for /dev/null
260+               """
261+       
262+               def write(self, s):
263+                       pass