![]() |
Home | Libraries | People | FAQ | More |
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 |
|---|---|
|
The 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 |
|---|---|
A CGI request is effectively closed when the application exits, so you
don't need to call |
Now we have a "Hello world" example compiling and working with our server we can choose to do two things:
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 |
|---|---|
Once you have set up an |
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 |
|---|---|
|
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 |
|---|---|
There are non- |
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.