Getting Request URI and Content in C++ FCGI

March 24, 2012

Previously I had mentioned how to create a simple hello world application in c++ with fcgi. However this simple application doesn’t allow for any inputs. This post we’ll show how retrieve the request uri accessed as well as the POST body content.

Getting the URI and POST content

Looking at the previous app, the output was a very simple hello, world. We will add functionality which spits back out the path that we hit the server with. Our desired functionality looks like:

> curl -d "Chris" http://localhost/foobar
<html>
  <head>
    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello Chris from /foobar !</h1>
  </body>
</html>

Notice that we respond with Hello <post content> from <request uri>. In order to do this we will have to capture the request uri and the post content. Let’s start off with the easier one which is the request uri. The request uri is buried within the FCGX_Request’s environment parameters. We can extract it using the FCGX_GetParam function like so:

const char * uri = FCGX_GetParam("REQUEST_URI", request.envp);

Note how we get the param ‘REQUEST_URI’ as named in our nginx configuration.

The slightly trickier part is getting the request content. We will write a helper function to do this for us. We start by determining the CONTENT_LENGTH of the response. If missing we assume it to be 0 since it is not safe to read from the fcgi request if it is missing. We limit the content length to ensure we don’t run out of memory and set it to an arbitrary high value (STDIN_MAX). If we can’t parse the content length we will just assume the max number of bytes.

This starts the function off with:

string get_request_content(const FCGX_Request & request) {
    char * content_length_str = FCGX_GetParam("CONTENT_LENGTH",
                                               request.envp);
    unsigned long content_length = STDIN_MAX;

    if (content_length_str) {
        content_length = strtol(content_length_str,
                                &content_length_str,
                                10);

        if (*content_length_str) {
            cerr << "Can't Parse 'CONTENT_LENGTH='"
                 << FCGX_GetParam("CONTENT_LENGTH", request.envp)
                 << "'. Consuming stdin up to " << STDIN_MAX << endl;
        }

        if (content_length > STDIN_MAX) {
            content_length = STDIN_MAX;
        }
    } else {
        // Do not read from stdin if CONTENT_LENGTH is missing
        content_length = 0;
    }
    ...
}

Now we have the content length we may actually read from standard input, up to the content_length number of characters.

...
    char * content_buffer = new char[content_length];
    cin.read(content_buffer, content_length);
    content_length = cin.gcount();
...

As a caveat of mod_fastcgi (currently not being used but nevertheless it can’t hurt to be a bit defensive) is that we need to consume the rest of stdin for the request because it doesn’t handle the request correctly otherwise.

...
    // ignore() doesn't set the eof bit in some versions of glibc++
    // so use gcount() instead of eof()...
    do cin.ignore(1024); while (cin.gcount() == 1024);
...

Finally we can package up the content into a string and free the allocated memory for our content_buffer.

...
    string content(content_buffer, content_length);
    delete [] content_buffer;
    return content;
}

End Result

Adding some simple changes to call this function and get the uri we end up with a fcgi program that takes http input! Download final version here (main_v2.cpp)

To run this code we recompile with main_v2.cpp and spawn the fcgi process again (link).

If we use curl to post to the site we get:

> curl -d "chris" http://localhost/foobar
<html>
  <head>
    <title>Hello, World!</title>
  </head>
  <body>
    <h1>Hello chris from /foobar !</h1>
  </body>
</html>

And a simple fcgi app in c++ is done!


Click to load comments