QuickStart

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


Subpages (1): EchoDebugLog
Comments