The FastCGI FAQ

Introduction

The goal of this FAQ is to complement the other documentation that has been written for FastCGI. Where the existing documentation has a hole, we'll plug the hole using the FAQ right away and migrate the words to another document later. Where some bit of information is written down but in an obscure place, we'll put a pointer to that obscure place in the FAQ right away and move the words to a more logical place later.

Please send suggestions for improving this FAQ to gambarin@openmarket.com. Especially welcome are questions that you struggled to answer, with your answer.


General

  1. What is FastCGI?

    FastCGI is an extension of CGI which eliminates CGI drawbacks and provides high performance, while remaining highly compatible with the existing CGI applications. See the page http://www.fastcgi.com/kit/doc/fastcgi-whitepaper/fastcgi.htm for a more detailed overview of FastCGI.

  2. What software do I need in order to use FastCGI?

    To use FastCGI you need a server module and an application library.

    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.

  3. How is FastCGI software supported?

    If you buy FastCGI software, you are probably paying for support, so you should follow your software vendor's support procedures.

    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:

Application Development

  1. How do I port a CGI app to FastCGI?

    To illustrate the issues in porting CGI apps to FastCGI we'll port a Perl script that does the following:

    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
        

  2. How do I debug my FastCGI app?

    Since FastCGI applications are started from the Web server and are run dissociated from a controlling terminal, some of the debugging techniques you are used to won't work. But there are replacements.

    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.

System Configuration

  1. How do I configure a distributed FastCGI app?

    We'll demonstrate how to configure a distributed FastCGI app using our Perl script that reports and logs the information from the 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.