Please send suggestions for improving this FAQ to gambarin@openmarket.com. Especially welcome are questions that you struggled to answer, with your answer.
The
FastCGI servers
page lists all the server modules that are currently
available. An additional server module, the
cgi-fcgi program, runs on any Web server that
supports CGI and is included in the
FastCGI developer's
kit.
The FastCGI applibs page lists all the application libraries that are currently available.
If you use free FastCGI software, you can't expect formal support. But there is an active FastCGI mailing list where you can ask questions, report problems, and make suggestions. The FastCGI mail page provides instructions for adding yourself to and removing yourself from the mailing list. The FastCGI web site also includes an archive of messages sent to the FastCGI mailing list.
If you are having a software problem, you'll greatly improve your chances of getting help by providing the following information in your message to the mailing list:
mod_fastcgi, you would report the version
of Apache you are running and the name and version of
the platform (e.g. "Solaris 2.5.1") you are running.
The Unix command uname -rs prints out
platform version information.
uptime command to obtain information about system
load average
uptime output and displays it in a Web page
uptime output to the file
This is not a serious application but it does highlight many porting issues. First, here's the CGI app:
#!/usr/local/bin/perl
# print HTTP header
print "Content-type: text/html\r\n\r\n";
# open the pipe and execute uptime command
open (INPUT, "uptime|") || exit(1);
# get the information from the uptime command
$load = <INPUT> ;
# extract and format
$load =~ s/^\s+|\s+$//;
if ($load =~ /average:/) {
$avgs = $';
@avgs = split (/, /, $avgs);
$avgs[0] =~ s/^\s+//;
print "\n";
print "Load 1 minute ago was : $avgs[0]\n";
print "Load 5 minutes ago was : $avgs[1]\n";
print "Load 15 minutes ago was : $avgs[2]\n";
}
# log to file
open (OUTPUT, ">>mylog.log") || die ("Unable to open output");
select (OUTPUT);
print "\n";
print "Load 1 minute ago was : $avgs[0]\n";
print "Load 5 minutes ago was : $avgs[1]\n";
print "Load 15 minutes ago was : $avgs[2]\n";
($sec, $min, $hour, $day, $month, $year, @stuff) =
localtime(time);
print "Logged on $month/$day/$year at $hour:$min:$sec\n";
return (0);
Every FastCGI app has the following skeleton:
So the first step in any conversion effort is to identify processing that can be done once, during initialization, and processing that must be repeated for each request. The more that can be done during initialization the faster your app will run. For instance, if you can open a file or a database during initialization, that's the place to do it.
During initialization, your program does not have access to the standard
input, output, and error streams. So if your program needs to report a problem
during initialization, it must do so either by writing to a log file
(e.g. syslog) or by exiting with an exit status that
identifies the problem.
Once your program enters its request-processing loop, it
should exit only when something is seriously wrong with the
process -- not when something is wrong with a particular
request. CGI programs often handle exceptional conditions by
spitting out an error screen and then calling
exit() or die(). Since the whole
idea of FastCGI is to run the application as a persistent
process, a FastCGI application shouldn't behave like this.
Instead, the application should return control to the main
request-processing loop after handling an exceptional
condition. Most languages contain mechanisms
(exceptions/handlers, longjmp, etc.) that help
you write this sort of code.
In a FastCGI application, the standard input, output, and error streams
no longer correspond one-to-one with file descriptors. On each
request, the FastCGI application library obtains a single file
descriptor over which it multiplexes input, output, and error information.
Therefore any application code that reaches behind the standard
streams and attempts to manipulate the underlying file descriptors
won't work with FastCGI. For instance, you can't use Perl's
sysread(STDIN) or syswrite(STDOUT).
Fortunately, there is no loss of functionality in using
the stream-based functions such as print.
Because FastCGI applications run as persistent processes, they must be careful not to leak resources such as memory and file descriptors. Most CGI programs don't worry about managing these resources because when the process terminates the OS takes care of releasing them. If it is too difficult to fix a resource leak in your FastCGI app, you can work around the problem by keeping track of the leak (e.g. by counting requests) and exiting the app before the leak gets out of hand. Your application manager (typically the FastCGI module in your Web server) will restart the application for you. Your app should call the FastCGI finish function to complete the current request before exiting; otherwise the unlucky requestor that causes the app to exit will see a server error.
Some CGI apps rely on exit to flush
all stream buffers at the end of each request. Since a FastCGI
app doesn't call exit at the end of each request,
it needs to get this functionality some other way. For instance,
if a FastCGI app writes to a log file stream on each request, it may want
to use an unbuffered stream or
flush the stream at the end of each request, so the log
file is up to date should the application crash. (There is
no need to flush the standard output and error streams; the FastCGI
application library takes care of that.)
Keeping these points in mind, here's how we rewrite our Perl CGI app to use FastCGI:
#!/usr/local/bin/perl
# Tell Perl to use code in FCGI module
use FCGI;
# Initialization code,
#
# REPLACE: open (OUTPUT, ">>mylog.log")||die ("Unable to open output");
# ^^^^^^^
if (!(open (OUTPUT, ">>mylog.log"))) {
# We shouldn't just terminate the process. If the log file
# isn't available, we'll do without it by writing to /dev/null:
open (OUTPUT, ">/dev/null");
}
# Here starts the main request-processing loop
while (FCGI::accept()>=0) {
# new request, so deal with it
# print HTTP header
print "Content-type: text/html\r\n\r\n";
# open the pipe
#
# REPLACE: open (INPUT, "uptime|") || exit(1);
# ^^^^^^^
if (!(open (INPUT, "uptime|"))) {
# We shouldn't just terminate the process. Instead,
# we'll display an error message and
# go back to the top of the loop.
print "Unable to obtain information from uptime";
next;
}
# get the information
$load = <INPUT> ;
# must close the pipe once we are done with it in order
# to prevent a file descriptor leaks.
close (INPUT);
# extract and format
$load =~ s/^\s+|\s+$//;
if ($load =~ /average:/) {
$avgs = $';
@avgs = split (/, /, $avgs);
$avgs[0] =~ s/^\s+//;
print "\n";
print "Load 1 minute ago was : $avgs[0]\n";
print "Load 5 minutes ago was : $avgs[1]\n";
print "Load 15 minutes ago was : $avgs[2]\n";
}
# log to file. The simplest way to ensure that the
# logged information makes it to the file is to set the
# log stream to unbuffered. A higher-performance
# way to do it is to explicitly flush the stream,
# but Perl doesn't provide us this option.
# XXX: right?
select (OUTPUT);
$| = 1;
print "\n";
print "Load 1 minute ago was : $avgs[0]\n";
print "Load 5 minutes ago was : $avgs[1]\n";
print "Load 15 minutes ago was : $avgs[2]\n";
($sec, $min, $hour, $day, $month, $year, @stuff) =
localtime(time);
print "Logged on $month/$day/$year at $hour:$min:$sec\n";
# reset the output to STDOUT, so that the
# next print does not write to the log file
select (STDOUT);
# REPLACE: return(0);
# ^^^^^^^
# the end of the request loop functions for FastCGI like
# return did for CGI
}
# When FCGI::accept returns an error, just exit
If you are accustomed to using an interactive
debugger such as gdb or dbx, you
can still use these debuggers on FastCGI apps.
As usual, you must compile the app for debugging
(e.g. -g switch to gcc)
in order to get the best use of the debugger. Also, your debugger
must be able to attach to a running process. Some platforms
don't allow attaching to a running process, but most do.
A typical command-line syntax
for attaching to a running process is
gdb prog_name pid
where prog_name is the name of the executable and
pid is the process
number. Consult your debugger's documentation for full information.Sometimes you will want to attach the debugger to your app before it has performed its initialization. One way to enable this is to make your main program look something\ like this:
void main(void)
{
int stopSleep = 0;
while (getenv("SLEEP") != NULL && stopSleep == 0) {
sleep(2);
}
/* the application proper starts here */
.
.
.
}
Now if you need to debug the application, start it with
AppClass ... -initial-env SLEEP=TRUE.
This will place the application into the sleep loop as soon as it starts.
Then use ps to find the application process,
attach your debugger to the process, set whatever breakpoints you like, set
stopSleep to a non-zero value, and continue the
application. Here's a transcript, using dbx:
% dbx fcgi_debug 9488
...
Reading symbolic information for libmp.so.1
Reading symbolic information for libw.so.1
Attached to process 9488 >>>Press ^C here to force a signal<<<
stopped in _sigsuspend at 0xdf678004
_sigsuspend+0x4: ta 0x8
Current function is main
9 sleep(2);
(dbx) assign stopSleep=1
(dbx) next
stopped in main at line 12 in file "fcgi_debug.c"
12 while(FCGI_Accept() >= 0) {
(dbx)quit
detaching from process 9488
%
Some programming languages used with FastCGI (e.g. Perl) do
not support attaching debuggers. You perform debugging in
these languages by inserting print statements. Before
entering the request loop, you can print to
syslog or to a log file that you open within the
application (most likely controlled by an environment variable
like SLEEP above.) Within the request loop you
have the additional options of printing to the standard output
and error streams. Anything you print to
standard output will go to the client, typically a browser.
You may want to format your debug printing to standard output
as HTML comments (e.g. <!-- this is a comment
-->), which you can look at using the "View/Document
Source" entry on the browser's menu.
Anything you print to standard error will
go to the server's error log file.
uptime command. The idea is to run the Perl script on
one machine and access the script from another machine.
With FastCGI, no changes to the script are needed to make it remotely
accessible.
We can start and access the program using different modes of the
cgi-fcgi
utility. Assume that cgi-fcgi is
located in directory $PATH, the Perl script is named
uptime.pl,
which is located in the directory $SCRIPT_PATH and that
we want this script to run on the port $PORT of the host machine
daffy.
Login to the remote machine and execute the following command there:
daffy> $PATH/cgi-fcgi -start -connect :$PORT $SCRIPT_PATH/uptime.pl
This will start the FastCGI process on the machine daffy. Now,
login into another machine and execute the following command:
another> $PATH/cgi-fcgi -bind -connect daffy:$PORT
If everything is working as expected, you should recieve information
from the uptime command run on daffy.
To access this application from a Web server on another machine,
you need to create a script file that's equivalent to the command
you typed to the shell above. You could write a /bin/sh
script, but there's a better way, using the -f feature
of cgi-fcgi:
#! $PATH/cgi-fcgi -f
-bind -connect daffy:$PORT
Installing this script as a CGI program on your Web server
gives Web access to the remote FastCGI application.
Some Web server FastCGI modules support direct access to remote
FastCGI applications, in which case cgi-fcgi is not needed
to perform the remote access; consult the documentation for
your Web server's FastCGI module for information.
This FAQ was created and is maintained by
Stanley Gambarin (gambarin@openmarket.com) Last updated : Oct 15, 1996.