Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

FastCGI Quickstart

TODO: FastCGI Quickstart

FastCGI is a protocol that was designed with efficiency in mind. One of the key features of FastCGI is that it allows a CGI-like program to serve multiple requests and outlive the lifetime of any one request. The application communicates with the server using TCP sockets [4] and the data is binary-encoded, which can be parsed quickly.

For a full introduction on FastCGI, see the FastCGI White Paper.

This library supports FastCGI on both linux and Windows. Windows support is currently limited to external FastCGI handlers only.

10 minute intro

The following example is generated from the linked source file.

#include <iostream> // for std::cerr
#include <string>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
1
#include <boost/cgi/fcgi.hpp>
namespace fcgi = boost::fcgi;

2
namespace client
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    ///////////////////////////////////////////////////////////////////////////
    //  Our calculator grammar
    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator>
    struct calculator : qi::grammar<Iterator, float(), ascii::space_type>
    {
        calculator() : calculator::base_type(expression)
        {
            using qi::_val;
            using qi::_1;
            using qi::float_;

            expression =
                term                            [_val = _1]
                >> *(   ('+' >> term            [_val += _1])
                    |   ('-' >> term            [_val -= _1])
                    )
                ;

            term =
                factor                          [_val = _1]
                >> *(   ('*' >> factor          [_val *= _1])
                    |   ('/' >> factor          [_val /= _1])
                    )
                ;

            factor =
                float_                          [_val = _1]
                |   '(' >> expression           [_val = _1] >> ')'
                |   ('-' >> factor              [_val = -_1])
                |   ('+' >> factor              [_val = _1])
                ;
        }

        qi::rule<Iterator, float(), ascii::space_type> expression, term, factor;
    };
} // namespace client

3
int handle_request(fcgi::request& req)
{
4
  req.load(fcgi::parse_all);
5

  fcgi::response resp;6

  if (req.form.count("expression"))
  {
    resp<< "<fieldset><legend>Result</legend><pre>";
        
    using boost::spirit::ascii::space;
    typedef std::string::const_iterator iterator_type;
    typedef client::calculator<iterator_type> calculator;

    calculator calc; // Our grammar

    std::string str ( req.form["expression"] );
    float result;
    if (!str.empty())
    {
        std::string::const_iterator iter = str.begin();
        std::string::const_iterator end = str.end();
        bool r = phrase_parse(iter, end, calc, space, result);

        if (r && iter == end)
        {
            resp << "-------------------------\n";
            resp << "Parsing succeeded\n";
            resp << "result = " << result << '\n';
            resp << "-------------------------\n";
        }
        else
        {
            std::string rest(iter, end);
            resp << "-------------------------\n";
            resp << "Parsing failed\n";
            resp << "stopped at: \": " << rest << "\"\n";
            resp << "-------------------------\n";
        }
    }
    else
    {
      resp<< "No expression found.";
    }
    resp<< "</pre></fieldset>";
  }
  resp<< "<form method='post' id=''>"
      << "  Expression: <input type='text' name='expression' value='"
      <<   req.form["expression"] << "'><br />"
      << "  <input type='submit' value='Calculate!'>"
      << "</form>"
      << fcgi::content_type("text/html");
7
  return fcgi::commit(req, resp);
}

8
int main(int, char**)
{
  fcgi::service service; 9
  fcgi::acceptor acceptor(service); 10
  int status;
  
11
  do {
12
    status = acceptor.accept(&handle_request);
    if (status)
        std::cerr
            << "Request handled with error. Exit code: "
            << status << std::endl;
  } while (!status);
  return status;
}

1

A catch-all header is available which includes all of the headers you should need for FastCGI.

For the sake of clarity we alias the boost::fcgi namespace rather than dumping all of the library names with a using namespace. This way, you can see what comes from the library.

2

The following code is taken straight from the calc3 example in Boost.Spirit. The only difference is to use floats rather than ints.

3

The first thing to do is write a handler function which takes a request and a response and does all request-specific work. Later, we will look at writing the code that calls this function.

4

A FastCGI request is not loaded or parsed by default.

5

Now that the request has been loaded, we can access all of the request data. The request data is available using std::map<>-like public members of a fcgi::request. [5]

A FastCGI request has several types of variables available. These are listed in the table below, assuming that req is an instance of fcgi::request:

Source

Variable

Description

Environment

req.env

The environment of a FastCGI request contains most of the information you will need to handle a request. There is a basic set of common environment variables that you can expect to be set by most HTTP servers around. A list of them is available on the _TODO_ (link) variables page.

GET

req.get

The variables passed in the query string of an HTTP GET request. The get_data map is multivalued and mimics a std::multimap<>.

POST

req.post

The HTTP POST data that is sent in an HTTP request's body. For file uploads, only the filename is stored in this map. As with get, the post_data map is multivalued and mimics a std::multimap<>.

Cookies

req.cookies

Cookies are sent in the HTTP_COOKIE environment variable. These can store limited amounts session information on the client's machine, such as database session ids or tracking information. As with get, the cookie_data map is multivalued and mimics a std::multimap<>.

File Uploads

req.uploads

File uploads, sent in an HTTP POST where the body is MIME-encoded as multipart/form-data. Uploaded files are read onto the server's file system. The value of an upload variable is the path of the file. The upload_data map is also multivalued.

Form

req.form

The form variables are either the GET variables or the POST variables, depending on the request method of the request.

6

The response class provides a streaming interface for writing replies. You can write to the request object directly, but for now we're going to use the response, which works well for most situations.

As you can see, the response is decoupled from the rest of the library. The advantages of using it over any other custom container are separate handling of the response body and headers, and a send() function which wraps the whole response and writes it out to the client who instigated the request.

Writing to a response is buffered. If an error occurs, you can clear() the response and send an error message instead. Buffered writing may not always suit your use-case (eg. returning large files), but when memory is not at a real premium, buffering the response is highly preferable.

Not only does buffering help with network latency issues, but being able to cancel the response and send another at any time is almost essential when an error can crop up at any time. A cgi::response is not tied to a request, so the same response can be reused across multiple requests. [6]

When sending a response that is large relative to the amount of memory available to the system, you may need to write unbuffered.

7

Finally, send the response back and close the request.

8

We now have a request handler in all of it's contrived glory.

The program's main function needs to parse the request, call the request handler defined above, and finally send the response.

9

A Service handles asynchronous operations and some of the protocol-specific bits.

10

An Acceptor handles accepting requests and little else.

11

Keep accepting requests until the handler returns an error.

12

The function boost::fcgi::acceptor::accept has a few overloads. The one used here takes a function or function object with the signature:

boost::function<int (boost::fcgi::request&)>

ie. A function that takes a reference to a request and returns an int. The returned int should be non-zero if the request was handled with an error.



[4] This library currently only supports TCP sockets. The FastCGI protocol allows for "any reliable transport" and adding support for named pipes on Windows and UNIX sockets on linux is on the TODO list.

[5] The data is stored internally in a single fusion::vector<>

[6] Not with plain CGI though, of course.


PrevUpHomeNext