Re: fastcgi event specification

Mark Brown (mbrown@OpenMarket.com)
Thu, 09 May 1996 20:50:28 -0400

Message-Id: <199605100050.UAA06554@breckenridge.openmarket.com>
To: Felix Gallo <fsg@coriolan.amicus.com>
Subject: Re: fastcgi event specification 
In-Reply-To: Your message of "Thu, 09 May 1996 16:34:30 CDT."
             <199605092134.QAA29296@coriolan.amicus.com> 
Date: Thu, 09 May 1996 20:50:28 -0400
From: Mark Brown <mbrown@OpenMarket.com>


Felix,

Thanks for reading the spec and sending your comments.

    1.  The server receives a request.

    2.  The server figures out which socket to start a transaction over.

    Unresolved: how does it do this, especially in light of the
    'affinity' concept?  What happens if you have more CGIs than
    your operating system permits for one process to have open
    streams or file descriptors (e.g., under Linux, FOPEN_MAX is 256,
    but POSIX minimum STREAM_MAX appears to be a paltry 16)?  Why
    are unix domain sockets (not existent in several very popular
    SVR3 Unix variants, such as SCO 3.x.x) included but named pipes not?

The details of binding requests to applications are Web server
specific.  For instance, Web servers in the NCSA family tend to be
file-system oriented, so the URL might resolve to a file with a
special MIME type signifying FastCGI.  The Open Market server's config
is URL-space oriented, so FastCGI app configuration is done by
specifying a URL prefix that maps to an application.

I'm not familiar with STREAM_MAX, but the Unixes we've ported
to all have FD limits that can be configured well beyond 16.

The Unixes we've ported to, even the SVR3 variants, all support
Unix domain sockets.  Unix domain sockets simplify the app
libraries a lot because they hide practically all the differences
between local and remote communication.  If some important
systems don't support Unix domain sockets, the spec will have
to be extended to named pipes.  Named pipes will add quite a bit
of complexity to the app libs, but so be it.

    3.  The server opens a listening socket file descriptor, closes
    all the rest of its file descriptors, and sets up, forks and execs
    a fastcgi application.

    Unresolved: may a server ever call connect before forking?

First off I should point out that the Web server does not
do this work on each request.  The Web server typically starts
up a pool of processes and just connects to them as requests 
come in.  The Web server also has the option of keeping
connections open; this works best with Web servers that
are implemented as a single process or a very small number
of processes.

A Web server can also connect to applications managed by some other
program, e.g. on another machine.  That machine might not even have a
Web server available.

When starting an application Web server opens the listening socket,
then forks, then closes other FDs, then execs the app.

Calling (non-blocking) connect before forking might work OK, I'm
not sure.  A blocking connect before forking would block forever,
not good.  Calling connect after forking seems like the straightforward
way to do it.

    4.  The server connects to the application.

    Unresolved: how does the application have any idea what servers
    are permitted to connect to that application?  Wouldn't this be
    a good use of the otherwise-avoided environment variables?

The application is being managed by something or someone who
takes care of most access control issues.

In case of local communication the access control is presumed done via
file system protections.  E.g. the directories containing listening
sockets are only readable by the httpd account.

In case of remote communication the application manager
sets up the FCGI_WEB_SERVER_ADDRS variable so the application
can perform IP address validation.  This is a reasonable
level of security on a well-managed LAN from Web server to app.

There's room for big improvements in this area, e.g. true
mutual authentication and secure connections to allow
communication over the public Internet.  Such complexity
was just too much for an initial proposal.

    5.  The server sends the application a FCGI_BEGIN_REQUEST packet.

    Unresolved: how is the server supposed to have any idea what
    role the application is meant to play?  What behaviors occur
    if the application plays a different role from the expected
    role?  Wouldn't it make more sense for the application to
    provide the server with the possible roles it can play before
    the server sets policy?

If there's a mismatch between the way the application's configured by
the Web server and the application's true capabilities, errors will
occur.  If the application gets sufficiently confused, the Web server
will notice illegal protocol sequences and shut down the connection.

A correctly-written application begins by checking its FCGI_ROLE
variable upon return from FCGI_Accept.  If the role is improper, the
application should give up on the request immediately.

My expectation is that the variable set understood by FCGI_GET_VALUES
will expand in future protocol versions to permit earlier detection of
this sort of configuration error for applications that wish, but it
might not be a good idea to require every application to support such
a feature.

    6.  The server sends a bunch of name/value pairs via FCGI_PARAMS
    to the application.

    Unresolved: why not set these in the environment as per
    'regular cgi' before the fork?  Surely the sets are faster
    than the protocol, and surely the params are going to be
    passed in 99% of the cases.

The normal environment variable mechanism doesn't work for
passing per-request parameters to a long-running server, as
with FastCGI.  Environment variables are used to initialize
the server, then FCGI_PARAMS is used to pass per-request
parameters for as long as the server stays up.

Interestingly, doing this communication using FastCGI protocol
appears to be *faster* than using the OS environment variable
facilities, at least on some platforms.  Go figure.

    7.  The server and application happily mediate, perhaps even 
    interleaving requests.

    Unresolved: is the ability to handle interleaved requests
    required of the application?  Can the application demand
    that it not be given interleaved requests?  What methods
    does the application use to signal to the server what
    behaviors can be expected of it?

It is definitely not required that applications handle
multiplexing -- way to complicated a mechanism to impose
on everyone.

An application that can't handle multiplexed connections
sends {FCGI_END_REQUEST, R, {0, FCGI_CANT_MPX_CONN}}
in response to the Web server's attempt to begin a new
request R while another request is in progress.  See
Section 5.5 of the spec.

Also, such an application returns FCGI_MPXS_CONNS=0 in response
to FCGI_GET_VALUES.   This allows the Web server to figure
out ahead of time that the application will reject multiplexing.

    8.  Depending on the server's expectation and the progress of the
    transaction, the server or the application 'close the connection'
    (terminate, however temporarily, the discussion).

    Unresolved: are the server or the application ever permitted
    to use the close system call on their respective ends of the
    filehandle?  If so, must the application attempt to return to
    a state where the server can make a connection using the same
    information the server had when it created the LISTEN socket?

Closing transport connections is the topic of Section 3.5
of the spec.  Applications should tolerate "unexpected" closing
by the Web server if possible.  The application always has the
option of crashing and being restarted by its application manager,
but that should be considered a last resort!

    What behaviors are truly mandatory for every conformant implementation
    of a fastcgi-aware application?

That question is a little too open-ended for me.  If you have some
particular questions I'll try to answer them.

It is definitely *not* required that fastcgi-aware applications
implement the CGI compatibility mode implemented by fcgi_stdio,
although it seems like a good idea when it is not too much trouble.

    Why doesn't the name/value pair protocol use a single fixed-length
    (4/4) header instead of requiring implementations to use byte-at-
    a time reading, comparison, shifting and branching to determine
    which header style is in use?  Given the the value length is
    probably not going be near 4294967296 bytes on any box, why '4'
    besides word alignment, and is transmitting that extra byte or two
    really worth it?

A 4/4 format might be attacked for being less space efficient than the
traditional Unix "name=value\000" format.  A 2/2 format would prohibit
values longer than 64K, not a problem today but could be in the
future.  3 bytes can't be right.  The specified format is a compromise
that is as space efficient as the traditional Unix format yet allows
for future versions of HTTP that will allow binary data in headers.
The additional complexity from the variable-length aspect of the
format can be isolated pretty easily, as demonstrated by the current
implementation.

Thanks again for the comments.  If you have suggestions for making
the spec easier to understand, please send them.

    --mark