Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Tutorial

"Hello, World."
The next step
Turning a CGI program into a FastCGI daemon

Let's start at the beginning, with the eponymous "Hello world" example:

#include <boost/cgi.hpp>
using namespace boost::cgi;

int main()
{
  request req;
  response resp; 

  resp<< "Hello, world.";

  return_(resp, req, 0); // note the underscore.
}

This is as simple as it gets. Don't worry though, the library isn't about having the shortest examples; it just so happens that hello world is easy to do. It is probably self-explanatory, but here is what is happening:

#include <boost/cgi.hpp>
using namespace boost::cgi;

The boost/cgi.hpp header includes the headers for CGI, aCGI (CGI with asynchronous I/O support) SCGI and FastCGI. If you only want the CGI stuff you can #include <boost/cgi/cgi.hpp>, or similarly for the other protocols (eg. <boost/cgi/fcgi.hpp> for FastCGI bits).

request req;
response resp;

A CGI script is quite simple in principle: the server accepts a request, spawns your CGI program to handle it and sends the response back to the client. These three concepts map directly to the library, with the request, response and client classes.

The request class (be it a boost::cgi::request or a boost::fcgi::request) is the heart of the library. The response class is a helper class that you can use similarly to std::cout, but with some important differences (more on this later). You aren't forced to use the response class with this library, but it's use is highly recommended. .

While a boost::cgi::request isn't directly compatible with a boost::fcgi::request, a response is independent of protocol and any request you choose to use it with - that makes it easy to reuse for different requests.

[Tip] Tip

The response class resides in the boost::cgi::common namespace and is aliased in the cgi/fcgi/etc. namespaces when including a protocol-specific header.

eg.

#include <boost/cgi/fcgi.hpp>


int main()
{
	boost::cgi::response resp1;
	boost::cgi::response resp2;
	
	assert(typeid(resp1) == typeid(resp2));
	return 0;
}

Default constructing a boost::cgi::request automatically parses everything you need (this is optional), so you can instantly write a response:

resp<< "Hello, world.";

You can stream data to a response just like you can with std::cout. In addition, you can stream things like content_type("text/html") and cookie("whatever", "value") or even redirect("http://google.com"). More on this later.

content_type() is one of the header factories provided by the library. In other words it is a function which returns a header object, so the following type lines do the same thing:

response resp;
resp<< content_type("text/plain")
	<< header("Content-type", "text/plain");	

Both are equivalent, but the former is more concise and (partly) checked at compile time making it less error prone. Note that the actual content type (ie. "text/plain") isn't checked at all - doing this seems unnecessary.

The response class understands headers and keeps them separate from the rest of the response. This allows you to add headers at any point, so long as you haven't flushed/sent the response already. For instance, you could add a custom header "Running-time" just before you send the response, which stated the time it took to handle the request.

As mentioned in the preface, all CGI scripts require at least a "Content-type" header. However, if you don't stream any headers to a response, a default "Content-type: text/plain" header is added. You can customise this using the macro BOOST_CGI_DEFAULT_CONTENT_TYPE.

#define BOOST_CGI_DEFAULT_CONTENT_TYPE "text/html" // Default output is HTML.

Since the response class buffers your data, so you must send it at some point. This doesn't happen automatically. It is sent to the Client associated with a request - ie. req.client(). A Client might actually be a connection to your HTTP server, but you should view it as a handle on a connection to the machine that made the request in the first place. You don't really need to know anything else about the Client concept for now. The library provides a convenient macro return_ to keep things clean:

return_(resp, req, 0);

This macro is equivalent to doing:

resp.send(req.client());
req.close(resp.status(), 0);
return 0; // Returns 0 to the OS

[Tip] Tip

A CGI request is effectively closed when the application exits, so you don't need to call close() on it. Since FastCGI and SCGI can handle multiple requests before the process exits you must explicitly close each one.

Now we have a "Hello world" example compiling and working with our server we can choose to do two things:

  • Make a CGI program that handles user-supplied data to provide dynamic content, or
  • Turn the CGI "Hello world" into a FastCGI "Hello world", allowing better scalability.

The first option is of course what CGI is all about: you take form data or a query string or a user's cookies and serve a custom response. The second option isn't mutually exclusive to the first, it is essentially an 'extra step' that you should be able to do at any point in the life-cycle of your application - although the sooner you do it the easier it might be!

[Important] Important

Once you have set up an fcgi::request, you use it in almost exactly the same way as a cgi::request.

branching?

So you want more flexibility and power than CGI can provide? This section shows you how to turn the CGI "Hello world" program into a FastCGI daemon that can handle more than one request at a time. We'll start off simple by handling one requests after another, before moving on to handling multiple requests simultaneously.

[Note] Note

The examples in this section assume you have prepended:

#include <boost/cgi/fcgi.hpp>
using namespace boost::fcgi;

int main()
{
  try
  {
    service s;
    acceptor a(s);
  
    for(;;)
    {
      request req(s);
      a.accept(req);

      req.load();
  
      response resp;
  
      resp<< "Hello, world.";
  
      resp.send(req.client());
      req.close(http::ok, 0);
    }
  
    return 0;

  }catch(boost::system::system_error& err){
    return 1;
  }
}

Hopefully you can guess what's going on above. Let's go through the differences to the original CGI example.

acceptor a(s);

This is what you use to accept requests.

for(;;)
{
  // ...
}

This is a simple example, so the program handles an arbitrary number of requests. You may choose to limit the number of requests you handle.

a.accept(req);

req.load(); // equivalent to req.load(parse_env) - the least-expensive useful state for a request.

This accepts one request. An accepted request is in a protocol-dependent state and generally isn't very useful. You must either load() or async_load() the request to put in a useful state. The arguments to these functions determine what is parsed. You can do things like:

req.load(parse_env|parse_post|parse_cookies); // parse everything but GET variables
req.load(parse_all); // parse everthing
req.load(parse_env); // parse just environment variables
req.load(parse_get); // parse environment and GET variables
req.load(parse_form); // parse environment and then either GET or POST variables, dependent on the REQUEST_METHOD

Beyond the accept/load sequence the request is basically the same as a CGI request as far as a library user is concerned. As mentioned earlier, you must explicitly close the request when you're finished with it:

req.close(http::ok, 0);

The first argument should be a valid HTTP status code, normalised to lower case and underscores except for '100 Continue', which is normalised to http::continue_ (because continue is a keyword). The second argument is the program status and should be what you would have put in the return 0; line at the end of your main() function. Zero is the default if the argument is omitted, while non-zero means an error which may cause the HTTP server to kill the process. An alternative way to pass the status is like so:

resp<< http::moved_permanently           // Handled specially by the `response`.
    << "This page is no longer valid."
    << location("somewhere_else")        // The location of the new page
    << content_type("text/plain");       // The location header has been output, so a content-type
																				 // header is needed now.
resp.send(req.client());
return req.close(resp.status());         // use return_() instead!

One final thing to note is the type of error thrown by this library, which comes from Boost.System:

try {
  // ...
}catch(boost::system::system_error& err){
  // err.message() <- string representing the error
  // err.id()      <- the error code (a number)
  // if(err) {}    <- only true when err represents an error
}

[Tip] Tip

There are non-throwing alternatives to most of the throwing functions in the library that take an additional boost::cgi::error_code argument. Due to the nature of FastCGI, exceptions may be undesireable - you won't usually want all the requests a process is handling to abort just because of a problem with one of them. In this case you can use the non-throwing versions of functions and gracefully close individual requests when something goes wrong.

The next example is very similar to the above but shows you how you might want to organise your program. As a demonstration it also uses the non-throwing alternatives to some functions.


PrevUpHomeNext