Python and FastCGI

John Nagle nagle at animats.com
Wed Dec 30 22:33:09 EST 2009


Steffen Ebermann wrote:
> Hi,
> 
> this might be a little off topic (could be any other language) 
> but I'm trying to set up Python to use FastCGI instead of plain CGI.
> 
> The basic setup is no problem but every tutorial (including Python's 
> 'HOWTO Use Python in the web') ends with some basic dispatch file and 
> that's it. I guess I should know how to go about from there, but,
> well I don't.
> 
> I guess I could put an execfile('index.py') into the dispatcher and
> then rewrite my code for FastCGI (i.e. replace every print with
> req.write() for jonpy), but that doesn't seem right to me.
> 
> Is there a better solution or do I misunderstand something entirely?
> I would be glad if someone could point me in the right direction.
> 
> Thanks in advance.

    You don't print in an FCGI program.  At the end of the function,
you return a reply string.

    Here's the main program of a live FCGI application.  This is
from something that answers XMLHttpRequest request, rather than
serving HTML pages.  So it outputs XML.

Things to note:

1. FCGI programs can run for a long time, like days. So you
have to handle the possible loss of a database connection, as
is done here.

2. Catch and handle all exceptions.  FCGI won't give you much
information in the error log.

3. You have to specify everything that goes in the HTTP headers
by calling start_response().

4. Notice the last line:
	WSGIServer(QuickSitetruthQuery).run()

That's the call which tells WSGIServer what function to call
for each request.

5. If you put an FCGI file in a directory from which Apache
will run CGI files, your FCGI program will be run as a CGI
program.  Everything will appear to work, because the FCGI
library understands this is a debug mode.  But you're loading
the program on each use, so the overhead is huge, as with CGI.
CGI is built into Apache at too low a level, and it will run
something as CGI even when you've told it a directory is
supposed to be used for FCGI.  So create an "fcgi" directory
someplace other than "cgi" and tell Apache to use that for
FCGI.

FCGI is a good system; it's just that the documentation isn't
very good.    This code has been running a live
web site for two years now.
	

					John Nagle

#!/usr/local/bin/python
### ... application-specific stuff deleted
from fcgi import WSGIServer
import MySQLdb
import cgi										import urllib
import InfoDisplay									import miscutils
db = None # database connection, held open for life of FCGI
#
#	The application
#
def QuickSitetruthQuery(environ, start_response):
     global db	# static global - active database handle
     try:
         if db :	# if previously attached
             try :
                 db.ping() # test whether connection is still up
             except MySQLdb.OperationalError, message:	# loss of conn.
                 db = None # we lost database connection
             if db is None : # if no valid database handle
                 db = miscutils.dbattach(kdbfile) # connect to database
             status = '200 OK' # normal status
             headers = [('Content-type','text/xml'), ('charset','utf-8')]
             reqlist = cgi.parse_qsl(environ['QUERY_STRING']) # Parse params
             priority = 1	# priority of request
             sourceip = environ['REMOTE_ADDR'] # get IP address of client
             urls = []	# list of URLs to check
             for item in reqlist :	# for all items
                 (key, value) = item	# extract item
                 if key.lower() == 'url' :	# want all "url" items,
                     urls.append(value)
                 elif key.lower() == 'priority' :	# if priority
                     priority = int(value)	# get priority value
             #	Make request; no waiting, no details
             outstr = InfoDisplay.getratingXMLquick(db, kdbfile,
                    urls, priority, sourceip) # get the rating XML
             needretry = outstr.find('status="202"') >= 0	# if retry soon
             if needretry :	# if the client will need to retry this request
                 headers.append(('Expires','-1')) # expire immediately
             start_response(status, headers)		# compose result
             s = kprefixxml + outstr + ksuffixxml	# construct output XML
             return [s.encode('utf8')]		# encode as UTF8
     except Exception, message:		# if trouble, report to user
         #	Error handling
         status = "500 Internal Error on Server"
         response_headers = [("Content-type","text/html")]
         start_response(status, response_headers)
         s = "<h1>Internal error - request not processed.</h1>\n\n" +
             traceback.format_exc()
         s = s.replace("\n","<br\>")		# convert to HTML
         return [s]
#
#    Main FCGI program
#
WSGIServer(QuickSitetruthQuery).run()



More information about the Python-list mailing list