The libfcgi2.dll addresses issues with the original libfcgi.dll with Windows Web Servers, most
notably the lack of a SIGTERM handler.
The Dll has been completely redesigned, exposing as many of the same functions as the original.
These are defined in the C Header file libfcgi2.h (or the BASIC include file FCGX_Header.inc)
To use this library, at a minimum, three function calls are required.
FCGX_InitRequest()
FCGX_Accept_r()
FCGX_PutStr()
(See the Hello World examples, or a more complete C++ example, or a BASIC complete example of a client application)
If you want the Request parameters you would additionally need to call
FCGX_GetParam()
And if the request contains some STDIN data (like from a POST method)
FCGX_GetStr()
All these functions work identically to the original dll, meaning the arguments
are the same, and the calling convention is the same. They are a drop in
replacement.
To use the library, the only change you will need to make is in the Structure
definitions for FCGX_REQUEST and FCGX_STREAM (see the header file)
For the SIGTERM notification event to be sent by the Web Server to this library
under IIS, the 'SignalBeforeTerminateSeconds' property in fcgiext.ini file must
be > 0. If it is not, the library will still work, but there will be no
opportunity for a graceful exit as no SIGTERM notification will be sent.
For testing, 'FCGIecho.exe' is included. This application will echo any GET
Query string and all POST data sent, and increment a counter for each request.
FCGX_REQUEST Structure:
Since we wrote this from the ground up, we took the opportunity to provide
access to some of the more relevant request parameters, and also implemented a
more powerful StringBuilder class for buffering stream data.
Since the QUERY_STRING request parameter is most often needed, we put an ASCIIZ
pointer to it right in the FCGX_REQUEST structure. To get the entire string
just de-reference the pointer 'pzQuery'. (This pointer is always valid and
never NUL). If the request does not contain a query string, it will point to an
ASCIIZ string of zero length.
NOTE: The return is just the value portion of the name=value pair contained in
the param array. This saves the step of parsing out the value.
Assigning this pointer is done at the lowest level when the FCGI name=value
pairs are decoded. At that level, it only requires a few additional steps to
put some variables at your fingertips. At the application level it is more
work. Because of this, we also added Request Method (ReqMethod) and
CONTENT_LENGTH (ContLen) to the FCGX_REQUEST structure.
The Request Variables are sent with *every* request and depending on the web
server, there can be over 3k of them. To minimize the amount of memory copy,
they are read directly into memory and an a pointer is assigned to each name/value
pair. A pointer to the array of pointers is stored in the structure variable ‘envp’
(legacy name!)
CONTENT_LENGTH is stored in the ‘ContLen’ member of the structure, and is a 4
byte signed integer.
Request Method is stored in the 'ReqMethod' property of the FCGX_REQUEST
structure as a 4 byte signed integer code. The codes are in the header file:
HTTP_GET = 1
HTTP_POST = 2
HTTP_HEAD = 3
HTTP_PUT = 4
HTTP_DELETE = 5
HTTP_OPTIONS = 6
HTTP_TRACE = 7
HTTP_CONNECT = 8
HTTP_UNKNOWN = 9
This makes using Request Method in a switch/select/if statement much faster
than comparing a string byte by byte for example.
The entire request parameter set is in an ASCIIZ string array (in exactly the
same way it was designed in the original libfcgi.dll), pointed to by the
pointer 'envp', meaning the pointer points to an array of ASCIIZ pointers. The
last pointer in the array is a NUL pointer indicating the end of the array.
Double de-referencing this pointer will return the name=value request
parameter.
To complete the request parameter variables, for convenience, we also added
'nParam'. This is a count of the name=value pairs in the array.
Next we have 'Role' and 'ConnFlags'. These were added in case we eventually
support other roles. For now the only role supported is Responder.
'ReqCount' is simply a cumulative count of the number if requests received in
liew of ID.
Finally, 'zVersion' will give you the version of the library as an integer
x1000. meaning 2089 would be 2.089
(The version is also contained in the Properties of the Dll)
FCGX_STREAM Streams:
For this dll, we wrote a custom implementation of the C++ String Builder class.
For those that are new to this concept, the class is a suite of functions that
manage memory for tasks like string concatenation (because concatenation is a
very slow way to append data to a string.)
This implementation starts with a small amount of memory and as more data is
added, the class automatically requests more memory to hold it, managing all
the memory allocation in the background. The available buffer length in memory,
is the 'Capacity', and the length of the data within that buffer memory is
'LenStored'. 'pData' points to the first byte of the buffer.
In managing memory, the class doubles the size of the buffer every time you
exceed 'Capacity' - this makes the most sense from a memory management standpoint as Joel Spolsky points out in his article on strings.
The string can be thought of, simply as a sequence of bytes that are available
as a buffer for your output. You can write to the buffer at any position, you
can read from it at any position, and you can increase its size and set the
LenStored value at any time. There are three buffers STDIN, STDOUT and STDERR
that can be used in this fashion.
The legacy reading and writing functions FCGX_GetStr(), FCGX_PutStr() read and
write sequentially, meaning when you write bytes, they are appended to whatever
is already in the buffer.
When you read bytes, the read where the last read left off. (The position
within the buffer is contained in the structure variable 'CurPos'. This will
have a value between 1 and LenStored and is reset at the begging of each new
request.)
Once your STDOUT buffer has been created, it can be sent to the Web Server by
calling FCGX_Flush(), or FCGX_Finish_r(). If neither are called and the application
simply loops back and calls FCGX_Accept_r() the buffer will be sent and then
emptied ready for the next Request.
New Functions:
It should be noted that FCGX_Flush() takes the request structure as an
argument, NOT a pointer to the stream structure like FCGX_FFlush did in the
original libfcgi.dll. FCGX_Flush() simply checks STDOUT and STDERR for any data
('LenStored' > 0) and sends it to the Web Server.
If you want to increase the size of a buffer, you must call FCGX_EnsureCap(). This
is the only way to accomplish this using the String Builder Class. The new
Capacity will be contained in the structure variable 'Capacity' and also
returned from the function call.
FCGX_URLDecode() is a very fast "in place" URL decoder, meaning the buffer
you send to it will be overwritten with the decoded string. The decoded length
(possibly shorter, never longer) is returned by the function.
FCGX_GetBytes() & FCGX_PutBytes() simply wrap a very fast ASM copymem
function for copying bytes to or from any of the buffers. For all the
variations tried (six register versions and eight mmx register versions) this
one still beats them all. The speed limit on memory copy is evidently imposed
by the actual speed of memory, however, the rep movsd pair is very well optimized
and has slightly less overhead than the others. It is up to you to make sure
your pointers are within the bounds of the buffer ('pData' to
'pData'+'Capacity'). These functions offer the flexibility to put/get bytes
anywhere within the buffer. (For languages that make use of BSTR, there are
also FCGX_Read and FCGX_Write.)
Errors:
If an exception is thrown, a numeric error code is generated along with a
description of the error. Depending on the error, this may be the result of
FormatMessage(GetLastError) a Pipe error or FCGI Library error. The Error code
will be returned as a -ve integer in all cases, and the description can be
obtained by dereferencing the pzLastErr pointer in the FCGX_REQUEST structure.
Library errors usually preclude returning an error via STDERR. This should be
used to report errors from your application (probably by printing them to a
text file somewhere outside the cgi-bin directory). Depending on the web server
settings, these errors may be written to the log or returned to the user.
Debugging:
For FCGI troubleshooting, a FCGI debug log may be enabled by calling
FCGX_InitRequest() with FCGX_DEBUG as the last parameter. This will create a directory of the root drive (C in this case)
C:\FCGIdebug
Each instance of the application will have a distinct pipe name and a distinct debug file name. The contents will be something like this debug from FCGIecho.exe