xCGI C++ Library

PrevUpHomeNext

Examples

CGI Examples
aCGI Examples
FastCGI Examples
Protocol-independent Examples

The examples can be built by going into their directory and typing bjam. If you have a server set up and wish to test the applications behind them, you can automatically copy them to your server's cgi-bin (or fcgi-bin or scgi-bin) one of two ways:

Command Line

# bjam install --cgi-bin=/usr/lib/cgi-bin

Installs the CGI program (or set of programs) into /usr/lib/cgi-bin. If you want to install FastCGI examples from the /libs/cgi/example/fcgi/ directory, for instance, type the above but replace --cgi-bin=... with --fcgi-bin=....

Environment Variables

Set the environment variable(s) BOOST_CGI_BIN_PATH (or BOOST_FCGI_BIN_PATH/BOOST_SCGI_BIN_PATH), then type:

# bjam install

The example(s) will be installed into the relevant directory.

[Note] Note

The command line option will override the environment variable, so you can use the environment variables for default installation.

See a demo of this online: cgi_hello_world

//
// The simplest CGI program, outputs only "Hello there, universe."
//
// If you think this is a pointless example, try getting it to work;
// It doesn't always go to plan... ;)
//

#include <boost/cgi/cgi.hpp>

using namespace boost::cgi;

int main()
{
  request req;      // Our request (POST data won't be parsed yet).
  response resp;    // A response object to make our lives easier.

  // This is a minimal response. The content_type(...) may go before or after
  // the response text.
  resp<< content_type("text/plain")
      << "Hello there, universe.";

  // Leave this function, after sending the response and closing the request.
  return_(resp, req, 0); // Note the underscore: returns "0" to the OS.
}

See the full source listing.

See a demo of this online: cgi_echo

//
// This example simply echoes all variables back to the user. ie.
// the environment and the parsed GET, POST and cookie variables.
// Note that GET and cookie variables come from the environment
// variables QUERY_STRING and HTTP_COOKIE respectively.
//

#include <boost/cgi/cgi.hpp>

using namespace boost::cgi;

//
// This function writes the title and map contents to the ostream in an
// HTML-encoded format (to make them easier on the eye).
//
template<typename OStreamT, typename MapT>
void show_map_contents(OStreamT& os, MapT& m, const std::string& title)
{
  os<< "<h3>" << title << "</h3>";
  
  if (m.empty())
    os<< "NONE<br />";
  else
    for (typename MapT::const_iterator i = m.begin(); i != m.end(); ++i)
      os<< "<b>" << i->first << "</b> = <i>" 
                 << i->second << "</i><br />";
}

int main()
{
  request req(parse_all); // A basic CGI request auto-parses everything (including POST data).
  response resp;

  resp<< "Request id = " << req.id() << "<p/>";

  show_map_contents(resp, req[env], "Environment Variables");
  show_map_contents(resp, req[get], "GET Variables");
  show_map_contents(resp, req[post], "POST Variables");
  show_map_contents(resp, req[cookies], "Cookie Variables");

  // Note that this (and any other) HTTP header can go either before or after
  // the response contents.
  resp<< content_type("text/html");

  // Send the response to the client associated with the request.
  resp.send(req.client());

  return 0;
}

See the full source listing.

Things in the acgi namespace are 'Asio-enabled' versions of those in the cgi namespace. The aCGI (a for asynchronous) parts of the library are especially useful if you are writing long-running CGI applications that could benefit from using asynchronous I/O or asynchronous event dispatching.

If not, there may be a small speed/size gain to be had from the plain CGI components.

See a demo of this online: acgi_hello_world

//
// The simplest CGI program, outputs only "Hello there, universe."
//

#include <boost/cgi/acgi.hpp>

using namespace std;
using namespace boost::acgi;

int main()
{
  service s;        // This becomes useful with async operations.
  request req(s);   // Our request (POST data won't be parsed yet).
  response resp;    // A response object to make our lives easier.

  // This is a minimal response. The content_type(...) may go before or after
  // the response text.
  resp<< content_type("text/html")
      << "Hello there, universe.";

  // Leave this function, after sending the response and closing the request.
  return_(resp, req, 0); // Note the underscore: returns "0" to the OS.
}

See the full source listing.

See a demo of this online: acgi_echo

//
// This example simply echoes all variables back to the user. ie.
// the environment and the parsed GET, POST and cookie variables.
// Note that GET and cookie variables come from the environment
// variables QUERY_STRING and HTTP_COOKIE respectively.
//

#include <fstream>
#include <cstdio>
#include <vector>
///////////////////////////////////////////////////////////
#include "boost/cgi/acgi.hpp"
#include "boost/cgi/utility/has_key.hpp"

using namespace std;
using namespace boost::acgi;

// The styling information for the page.
static const char* gCSS_text =
"body { padding: 0; margin: 3%; border-color: #efe; }"
".var_map_title"
    "{ font-weight: bold; font-size: large; }"
".var_map"
    "{ border: 1px dotted; padding: 2px 3px 2px 3px; margin-bottom: 3%; }"
".var_pair"
    "{ border-top: 1px dotted; overflow: auto; padding: 0; margin: 0; }"
".var_name"
    "{ position: relative; float: left; width: 30%; font-weight: bold; }"
".var_value"
    "{ position: relative; float: left; width: 65%; left: 1%;"
     " border-left: 1px solid; padding: 0 5px 0 5px;"
     " overflow: auto; white-space: pre; }"
;

//
// This function writes the title and map contents to the ostream in an
// HTML-encoded format (to make them easier on the eye).
//
template<typename OStreamT, typename MapT>
void format_map(OStreamT& os, MapT& m, const std::string& title)
{
  os<< "<div class=\"var_map\">"
         "<div class=\"var_map_title\">"
    <<       title
    <<   "</div>";

  if (m.empty())
    os<< "<div class=\"var_pair\">EMPTY</div>";
  else
    for (typename MapT::const_iterator i = m.begin(); i != m.end(); ++i)
    {
      os<< "<div class=\"var_pair\">"
             "<div class=\"var_name\">"
        <<       i->first
        <<   "</div>"
             "<div class=\"var_value\">"
        <<       i->second
        <<   "</div>"
           "</div>";
    }
  os<< "</div>";
}

std::size_t process_id()
{
#if defined(BOOST_WINDOWS)
  return _getpid();
#else
  return getpid();
#endif
}


int main()
{
  try{
    //std::ofstream of("c:/cc/log/cgi.error");
    //if (!of)
    //  throw std::runtime_error("Couldn't open file for writing");
    //std::cerr.rdbuf() = of.rdbuf();
    service s;
    request req(s);

    try {

      boost::system::error_code ec;
      req.load(parse_all, ec); // parse everything.

      if (ec)
      {
        response resp;
        resp<< content_type("text/html")
            << "Error " << ec.value() << ": " << ec.message() << "<p />"
               "--Original message follows--"
               "<p />";
        resp.send(req.client());
      }

      response resp;
      resp<< content_type("text/html") <<
             "<html>"
             "<head>"
               "<title>CGI Echo Example</title>"
               "<style type=\"text/css\">"
          <<       gCSS_text <<
               "</style>"
             "<head>"
             "<body>"
               "Request ID = " << req.id() << "<br />"
               "Process ID = " << process_id() << "<br />"
               "<form method=POST enctype='multipart/form-data'>"
                 "<input type=text name=name value='"
          <<         (has_key(req[post],"name") ? req[post]["name"] : "")
          <<      "' />"
                 "<br />"
                 "<input type=text name=hello value='"
          <<         (has_key(req[post],"hello") ? req[post]["hello"] : "")
          <<      "' />"
                 "<br />"
                 "<input type=file name=user_file />"
                 "<input type=hidden name=cmd value=multipart_test />"
                 "<br />"
                 "<input type=submit value=submit />"
               "</form><p />";

      format_map(resp, req[env], "Environment Variables");
      format_map(resp, req[get], "GET Variables");
      format_map(resp, req[post], "POST Variables");
      format_map(resp, req[cookies], "Cookie Variables");

      if (req["request_method"] == "GET")
      {
        resp<<   "<pre>";
        BOOST_FOREACH(char& ch, req.post_buffer())
        {
          resp<< ch;
        }
        resp<<   "</pre>"
               "</body>"
               "</html>";
      }

      return_(resp, req, 0); // All ok.

    }
    catch(boost::system::system_error& ec)
    { // This is the type of error this library throws.
      response resp;
      resp<< content_type("text/plain") << "Error " << ec.code() << ": "
          << ec.what()
          << http::internal_server_error; // note the status_code
      return_(resp, req, 1);
    }
    catch(std::exception* e)
    {
      response resp;
      resp<< content_type("text/plain") << "Error: " << e->what()
          << http::internal_server_error;
      return_(resp, req, 2);
    }

    // The request object will be destroyed before the next exception handlers
    // are reached.

  }catch(std::exception* e){
    std::cout
    << content_type("text/plain").content
    << "Exception: " << e->what();
    return 3;
  }catch(...){
    std::cout<< content_type("text/plain").content
             << "Unknown error.";
    return 4;
  }
}

See the full source listing.

See a demo of this online: acgi_amort

This example uses Google.cTemplate to move HTML out of the program and into HTML template files, which would be easily editable by anyone familiar with HTML.

[Note] Note

You need to download and install Google's cTemplate library and you may have to modify Jamfile.v2 to point to your compiled ctemplate library (see the lib ctemplate ... lines).

//
// Amortization Calculator
// -----------------------
//
// This file uses Google cTemplate to show the benefits of using an
// HTML template engine. The code isn't commented, but should be 
// reasonably self-explanatory.
//
// It is a very basic amortization calculator.
//
#include <iostream>
#include <iomanip>
#include <boost/cgi/acgi.hpp>
#include <boost/cgi/utility.hpp>
#include <boost/algorithm/string/regex.hpp>
#include <google/template.h>

using namespace boost::acgi;

#define DEFAULT_LOAN_AMT "$250,000"
#define DEFAULT_INTEREST_RATE "6.000"

/// Convert a string like '$250,000' into one like '250000'.
std::string string_from_currency(std::string amt)
{
  // this is much too hardcore, but it works fine...
  boost::algorithm::erase_all_regex(amt, boost::regex("[$, ]"));
  return amt;
}

template<typename Request, typename Dictionary>
void get_prepayment_frequency(Request& req, Dictionary& dict)
{
  std::string val(has_key(req[form], "PrePmtFreq")
                       ? req[form]["PrePmtFreq"]
                       : "Monthly");
  dict.SetValue("PrePmtFreq_" + val, " selected=\"selected\"");
}

template<typename Request, typename Dictionary>
double get_term_years(Request& req, Dictionary& dict)
{
  std::string val(has_key(req[form], "TermYrs")
                       ? req[form]["TermYrs"]
                       : "");
  dict.SetValue("TermYrs_" + val, " selected=\"selected\"");
  return boost::lexical_cast<double>(val);
}

template<typename Request, typename Dictionary>
double get_interest_rate(Request& req, Dictionary& dict)
{
  std::string val(has_key(req[form], "YearlyIntRate")
                       ? req[form]["YearlyIntRate"]
                       : DEFAULT_INTEREST_RATE);
  dict.SetValue("YearlyIntRate", val);
  return boost::lexical_cast<double>(val);
  
}

template<typename Request, typename Dictionary>
double get_loan_amount(Request& req, Dictionary& dict)
{
  std::string val(has_key(req[form], "LoanAmt")
                       ? req[form]["LoanAmt"]
                       : DEFAULT_LOAN_AMT);
  dict.SetValue("LoanAmt", val);
  return boost::lexical_cast<double>(string_from_currency(val));
}

/// This function fills the dictionary and sub-dictionaries with relevant values.
template<typename Request>
void fill_amortization_dictionary(google::TemplateDictionary& dict, Request& req)
{
  dict.SetValue("SCRIPT_NAME", req.script_name());

  get_prepayment_frequency(req, dict);

  if ( ! has_key(req[form], "Amortize") )
  {
    dict.ShowSection("NotAmortize");
    // Set some default values
    dict.SetValue("LoanAmt", DEFAULT_LOAN_AMT);
    dict.SetValue("YearlyIntRate", "6.000");
  }
  else
  {
    double P = get_loan_amount(req, dict);
    double i = get_interest_rate(req, dict) / 1200;
    double n = get_term_years(req, dict) * 12;
    double monthly_payments = (P*i) / (1 - std::pow((1+i), -n));
    
    google::TemplateDictionary* sub_dict = dict.AddSectionDictionary("RegPmtSummary");
    sub_dict->ShowSection("RegPmtSummary");
    sub_dict->SetFormattedValue("MonthlyPmt", "%.2f", monthly_payments);

    dict.ShowSection("Amortize");

    double balance (P);
    int row_num (0);
    double interest (0);
    double principal_paid (0);
    double total_interest (0);
    do{
      google::TemplateDictionary* sub_dict2 = dict.AddSectionDictionary("PaymentEntry");
      sub_dict2->ShowSection("PaymentEntry");
      sub_dict2->SetFormattedValue("Payment", "%.2f", monthly_payments);
      sub_dict2->SetIntValue("ROW_NUM", ++row_num);
      sub_dict2->SetIntValue("ROW_TYPE", (row_num % 2) + 1);
      interest = balance * i;
      total_interest += interest;
      sub_dict2->SetFormattedValue("InterestPaid", "%.2f", interest);
      principal_paid = monthly_payments - interest;
      sub_dict2->SetFormattedValue("PrincipalPaid", "%.2f", principal_paid);
      balance -= principal_paid; // Note: balance can increase.
      sub_dict2->SetFormattedValue("Balance", "%.2f", balance);

    }while(balance > 0);

    sub_dict->SetFormattedValue("RegPmt_TotalIntPd", "%.2f", total_interest);
    sub_dict->SetFormattedValue("RegPmt_TotalPmts", "%.2f", total_interest + P);
  }
}

template<typename Request>
void write_amortization_template(Request& req, response& resp)
{
  google::TemplateDictionary dict("amortization");

  fill_amortization_dictionary(dict, req);

  google::Template* tmpl
    = google::Template::GetTemplate("../templates/acgi_amort.html", google::STRIP_WHITESPACE);

  std::string arg(req[get]["arg"]);
  if (arg.empty())
    arg = "2"; // Make this the default

  // Depending on the value of `arg`, we can write the output in
  // different ways.

  if (arg == "1")
  {
    // Output the response straight to std::cout. This won't work with
    // anything but vanilla CGI and isn't recommended. It's useful for
    // checking things work though (don't forget to write a content type
    // header - followed by a blank line - first though!).
    std::string output;
    tmpl->Expand(&output, &dict);
    std::cout<< "Content-type: text/html\r\n\r\n"
             << output;
  }else
  if (arg == "2")
  {
    // Expand the output to a string and copy the string to the response.
    // Should be expensive, but doesn't seem to impact performance hugely...
    std::string output;
    tmpl->Expand(&output, &dict);
    resp<< content_type("text/html")
        << output;
  }else
  //  if (arg == "3")
  //  {
  //    // Expand the string to a vector<const_buffer>, which should minimise any
  //    // copying of data. This requires a modified version of Google.cTemplate, but
  //    // doesn't seem to add anything to performance. Will have to check if there's a
  //    // better way to do it. 
  //    std::string s;
  //    std::vector<boost::asio::const_buffer> out;
  //
  //   tmpl->Expand(&s, &out, &dict);
  //    write(req.client(), out);
  //  }else
  if (arg == "4")
  {
    // Write the output directly to the request's client.
    std::string headers ("Content-type: text/html\r\n\r\n");
    write(req.client(), buffer(headers));
    std::string output;
    tmpl->Expand(&output, &dict);
    write(req.client(), buffer(output));
  }else
  if (arg == "5")
  {
    // An alternative to { arg == "1" }, which seems to be slightly faster.
    std::string output;
    tmpl->Expand(&output, &dict);
    const char* headers = "Content-type: text/html\r\n\r\n";
    std::cout.write(headers, strlen(headers));
    std::cout.write(output.c_str(), output.size());
  }else
  {
    resp<< "Error!";
  }
}

int main()
{
  try{
    service s;
    request req(s);
    req.load(parse_all);
    response resp;

    write_amortization_template(req, resp);
    resp<< req[form]["YearlyIntRate"];
  
    return_(resp, req, 0);
  }catch(boost::system::system_error* err){
    std::cout<< "Content-type: text/plain\r\n\r\n"
             << "Error (" << err->code() << "): " << err->what();
    return 0;
  }catch(boost::system::system_error& err){
    std::cout<< "Content-type: text/plain\r\n\r\n"
             << "Error (" << err.code() << "): " << err.what();
    return 0;
  }catch(std::exception* e){
    std::cout<< "Content-type: text/html\r\n\r\n"
             << "Exception caught: " << e->what();
    return 0;
  }catch(std::exception& e){
    std::cout<< "Content-type: text/html\r\n\r\n"
             << "Exception caught: " << e.what();
    return 0;
  }catch(...){
    std::cout<< "Content-type: text/html\r\n\r\n"
             << "Unknown error!";    
    return 0;
  }
}

See the full source listing.

See a demo of this online: acgi_cookie_game

This example shows the user their cookies and allows them to set and delete them. You should take a look at the Cookie Game 2 example, which uses Google.cTemplate to move HTML out of the program and into HTML template files. This example mixes HTML and source code, which will generally cause you to waste a lot of time when making changes to structure and design of responses.

//
// Cookie Tests
// ------------
//
// This simple example shows the user their cookies and lets them set and
// delete them.
//
#include <boost/cgi/acgi.hpp>
#include <boost/cgi/utility.hpp>

// The styling information for the page.
static const char* gCSS_text =
"body { padding: 0; margin: 3%; border-color: #efe; }"
".var_map_title"
    "{ font-weight: bold; font-size: large; }"
".var_map"
    "{ border: 1px dotted; padding: 2px 3px 2px 3px; margin-bottom: 3%; }"
".pair"
    "{ border-top: 1px dotted; overflow: auto; padding: 0; margin: 0; }"
/*
".name"
    "{ position: relative; float: left; width: 30%; font-weight: bold; }"
".value"
    "{ position: relative; float: left; width: 65%; left: 1%;"
     " border-left: 1px solid; padding: 0 5px 0 5px;"
     " overflow: auto; white-space: pre; }"
*/
".info"
"{"
    "border-color: #ca3766;"
    "border-width: 1px 0 1px 0;"
    "border-style: solid;"
    "padding: 2px 8px 2px 8px;"
    "margin: 1em"
"}"
"#response"
"{"
    "text-align: center;"
    "padding: 20px 30px 20px 30px;"
    "margin: 1em;"
    "border-width: 2px 0 2px 0;"
    "border-style: solid;"
    "border-color: #ca3766;"
"}"
".var_map_title"
"{"
    "font-weight: bold;"
    "font-size: large;"
"}"
".var_map"
"{"
    "border: 1px dotted;"
    "padding: 2px 3px 2px 3px;"
    "margin: 15px 0 15px 0;"
"}"
".var_pair"
"{"
    "border-top: 1px dotted;"
    "overflow: auto;"
    "padding: 0;"
    "margin: 0;"
"}"
".var_name"
"{"
    "position: relative;"
    "float: left;"
    "width: 30%;"
    "font-weight: bold;"
"}"
".var_value"
"{"
    "position: relative;"
    "float: left;"
    "width: 65%;"
    "left: 1%;"
    "border-left: 1px solid;"
    "padding: 0 5px 0 5px;"
    "overflow: auto;"
    "white-space: pre;"
"}"
".nvpair"
"{"
    "list-style-type: none;"
"}"
"form"
"{"
//    "width: 450px;"
"}"
"input"
"{"
"border-width: 0 0 1px 0;"
"border-style: solid;"
"border-color: #444;"
"padding: 2px 3px 2px 3px;"
"margin-left: 1em;"
"background: #FaFbFd;"
"}"
"input[type=submit]"
"{"
    "margin-left: 35%;"
"}"
".name, .value"
"{"
    "display: inline;"
    "width: 20%;"
    "float: left;"
"}"
".name"
"{"
    "text-align: right;"
    "padding-right: 1em;"
    "clear: left;"
"}"
".value"
"{"
    "text-align: left;"
    "padding-left: 1em;"
"}"
".clear { clear: both; }"
".cookies { margin: 10px 0 25px 0; }"
"#postform"
"{" 
    "visibility: hidden;"
    "display: none;"
"}"
;

static const char* gJS_text =
"function switch_method(ident) {"
    "var f = document.getElementById(ident);"
    "if (f.method == 'post') f.method = 'GET';"
    "else f.method = 'POST';"
"}"
"function switch_value(elem) {"
    "if (elem.value == 'Switch to POST') {"
        "elem.value = 'Switch to GET';"
    "}else{"
        "elem.value = 'Switch to POST';"
    "}"
"}"
"function switch_form(elem) {"
    "var g = document.getElementById('getform');"
    "var p = document.getElementById('postform');"
    "if (elem.value == 'Switch to POST') {"
        "p.style.visibility = 'visible';"
        "p.style.display = 'block';"
        "g.style.visibility = 'hidden';"
        "g.style.display = 'none';"
        "elem.value = 'Switch to GET';"
    "} else {"
        "g.style.visibility = 'visible';"
        "g.style.display = 'block';"
        "p.style.visibility = 'hidden';"
        "p.style.display = 'none';"
        "elem.value = 'Switch to POST';"
    "}"
"}"
;

/**
 * The following example has a few stages.
 * It is best understood by compiling and testing it, and then looking at
 * the source code.
 */

using namespace boost::acgi;

template<typename Response, typename MapT>
void print_formatted_data(Response& resp, MapT& data)
{
    resp<< "<div class=\"cookies\">";
    if (data.empty())
        resp<< "<span class='value'>None found</span>";
    else {
        resp<< "<ul class=\"nvpair\">";
        for(typename MapT::const_iterator iter=data.begin(), end = data.end(); iter != end; ++iter)
        {
            resp<< "<li class=\"name\">"
                <<     iter->first
                << "</li>"
                   "<li class=\"value\">"
                <<     iter->second
                << "</li>";
        }
        resp<< "</ul>"
               "<div class=\"clear\"></div>";
    }
    resp<< "</div>";
}

int main()
{
  service srv;
  request req(srv);

  // Load up the request data
  req.load(parse_all);

  response resp;
  resp<< content_type("text/plain");

  if (has_key(req[form], "reset") && req[form]["reset"] == "true")
  {
    resp<< cookie("name")
        << redirect(req, req.script_name()); // redirect them.
    resp.send(req.client());
    return 0;
  }

  if (has_key(req[form], "name"))
  {
    if (has_key(req[form], "del"))
      resp<< cookie(req[form]["name"]);
    else
      resp<< cookie(req[form]["name"], req[form]["value"]);
    resp<< redirect(req, req.script_name());
    return_(resp, req, http::ok);
  }

  resp<< content_type("text/html")
      << "<html>"
         "<head>"
         "<style type=\"text/css\">"
      <<   gCSS_text
      << "</style>"
         "<script type=\"text/javascript\" language=\"Javascript\">"
      <<   gJS_text
      << "</script>"
         "</head>"
         "<body>";

  // First, see if they have a cookie set
  if (has_key(req[cookies], "name"))
      resp<< "<h1>Hello again " << req[cookies]["name"] << "</h1>"
          << "<p><a href='?reset=true'><input type='submit' value='Reset' /></a></p>";
  else
    resp<< "<h1>Hello there.</h1>";

  resp<< "<p>You can add cookies using the form below. If you add a cookie value for 'name', it will show up above.</p>"
         "<p>Here is list of the cookies you currently have:</p>";
  
  print_formatted_data(resp, req[cookies]);
  
  resp<< "<form method='get' action='" << req.script_name() << "' id='getform'>"
           "<label for='name' class='name'>Name:</label>"
           "<input id='name' name='name' class='value' type='text' value='" << req[form]["name"] << "'>""</input>"
           "<label for='value' class='name'>Value:</label>"
           "<input id='value' name='value' class='value' type='text' value='" << req[form]["value"] << "'>""</input>"
           "<label for='del' class='name'>Delete this cookie?</label>"
           "<input id='del' name='del' class='value' type='checkbox'></input>"
           "<div class='clear'></div>"
           "<input type='submit'></input>"
         "</form>"
         "<input type='submit' onclick='switch_method(\"getform\"); switch_value(this); return false;' value='Switch to POST'></input>"
         "</body>"
         "</html>";

  return_(resp, req, http::ok);
}

See the full source listing.

This example shows the user their cookies and allows them to set and delete them. It uses Google.cTemplate to move HTML out of the program and into HTML template files, which would be easily editable by anyone familiar with HTML.

If you're not familiar with the term, read up on MVC. The additional time you'll spend learning an HTML templating library, such as Google.cTemplate, will be saved many times over when compiling your CGI app starts taking more than a few seconds!

[Note] Note

You need to download and install Google's cTemplate library and you may have to modify Jamfile.v2 to point to your compiled ctemplate library (see the lib ctemplate ... lines).

You can try a demo of the MVC Cookie Game example here.

//
// Cookie Test With cTemplate
// --------------------------
//
// This file uses Google cTemplate to show the benefits of using an HTML
// template engine. Using cTemplate to separate how you show the response and
// how you figure out what to respond with, is keeping to the MVC paradigm.
// Read up on that if you're not familiar; if you already are, you can
// probably stop scowling at the last cookie_game example now.
//
#include <boost/cgi/acgi.hpp>
#include <boost/cgi/utility.hpp>
#include <google/template.h>

/**
 * The following example has a few stages.
 * It is best understood by compiling and testing it, and then looking at
 * the source code.
 */

using namespace boost::acgi;

// The types we use. Only here because this is an example.

// Uses cTemplate, from Google. It's simple and powerful.
typedef google::Template stencil_type;
// You will usually load a template and then populate variables in it
// using a TemplateDictionary.
typedef google::TemplateDictionary dictionary_type;
// The acgi and fcgi parts of the CGI library use a `service` class to 
// manage asynchronous dispatching (eg. async I/O). If you're not interested
// in async I/O, you can just use the plain cgi stuff (which is the same as
// acgi, but without the *a*sync bits).
// If you're unsure, you can use acgi without having to really do anything with
// the service - it's only used on two lines in this example. In one of them
// it is constructed...
typedef service service_type;
// ... and on the other it's used to construct the `request`.
typedef request request_type;
// The `response` will make your life easier (and more efficient).
typedef response response_type;

// These are some of the functions / types / enums used in this example.
using boost::acgi::has_key;
using boost::acgi::cookie;
using boost::acgi::header;
using boost::acgi::redirect;
using boost::acgi::parse_all;
using boost::acgi::form;
using boost::acgi::cookies;
using boost::acgi::content_type;

// This function just makes it easier to change the templating engine. It's
// only here to keep the cTemplate code out of the core of this example...
stencil_type* get_stencil(std::string const& filename)
{
  return google::Template::GetTemplate(filename, google::STRIP_WHITESPACE);
}

// Show the data in the passed map, updating the passed dictionary.
template<typename MapT, typename Dict>
void print_formatted_data(MapT& data, Dict& dict)
{
  Dict* subd = dict.AddSectionDictionary("DATA_MAP");
  if (data.empty())
    subd->ShowSection("EMPTY");
  else
    for(typename MapT::const_iterator iter=data.begin(), end = data.end(); iter != end; ++iter)
    {
      Dict* row_dict = subd->AddSectionDictionary("ROW");
      row_dict->SetValue("NAME", iter->first.c_str());
      row_dict->SetValue("VALUE", iter->second);
      row_dict->ShowSection("ROW");
    }
}


int main()
{
  try {
    service_type srv;
    request_type req(srv);

    // Load up the request data
    req.load(parse_all);

    response_type resp;

    if (has_key(req[form], "reset") && req[form]["reset"] == "true")
    {
      resp<< cookie("name")
          << redirect(req, req.script_name()); // redirect them.
      resp.send(req.client());
      return 0;
    }

    if (has_key(req[form], "name"))
    {
      if (has_key(req[form], "del"))
        resp<< cookie(req[form]["name"]);
      else
        resp<< cookie(req[form]["name"], req[form]["value"]);
      resp<< redirect(req, req.script_name());
      return_(resp, req, http::ok);
    }

    dictionary_type dict("cookie-game dict");

    // First, see if they have a cookie set
    if (has_key(req[cookies], "name"))
      dict.SetValueAndShowSection("USER_NAME", req[cookies]["name"], "HAS_NAME_IN_COOKIE_true");
    else
      dict.ShowSection("HAS_NAME_IN_COOKIE_false");

    print_formatted_data(req[cookies], dict);

    dict.SetValue("SCRIPT_NAME", req.script_name());  
    dict.SetValue("COOKIE_NAME", get_value(req[form], "name", ""));
    dict.SetValue("COOKIE_VALUE", req[form]["value"]);

    // Load the HTML stencil now from the index.html file.
    stencil_type* stencil = get_stencil("../templates/index.html");

    // Expand the stencil with the the given dictionary into `output`.
    std::string output;
    stencil->Expand(&output, &dict);

    // Add the template to the response.
    resp<< content_type("text/html")
        << output;

    // Send the response to the requestor and return control.
    return_(resp, req, http::ok);
    
  }catch(std::exception* e){
    std::cout<< "Exception: [" << typeid(e).name() << "] - " << e->what() << std::endl;
  }catch(std::exception const& e){
    std::cout<< "Exception: [" << typeid(e).name() << "] - " << e.what() << std::endl;
  }catch(...){
    std::cout<< "boom<blink>.</blink>";
  }
}

Table 1.3. Files used for this example:

File

Contents

main.cpp

The C++ source.

style.css

CSS styling for the response.

main.js

Javascript functions (for changing between GET and POST requests.

index.html

The HTML template itself.


//
// This example simply echoes all variables back to the user. ie.
// the environment and the parsed GET, POST and cookie variables.
// Note that GET and cookie variables come from the environment
// variables QUERY_STRING and HTTP_COOKIE respectively.
//
///////////////////////////////////////////////////////////
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/program_options/environment_iterator.hpp>
///////////////////////////////////////////////////////////
#include "boost/cgi/fcgi.hpp"

using namespace std;
using namespace boost::fcgi;

// This is a file to put internal logging info into
#define LOG_FILE "/var/www/log/fcgi_echo.txt"

// The styling information for the page, just to make things look nicer.
static const char* gCSS_text =
"body { padding: 0; margin: 3%; border-color: #efe; }"
".var_map_title"
    "{ font-weight: bold; font-size: large; }"
".var_map"
    "{ border: 1px dotted; padding: 2px 3px 2px 3px; margin-bottom: 3%; }"
".var_pair"
    "{ border-top: 1px dotted; overflow: auto; padding: 0; margin: 0; }"
".var_name"
    "{ position: relative; float: left; width: 30%; font-weight: bold; }"
".var_value"
    "{ position: relative; float: left; width: 65%; left: 1%;"
     " border-left: 1px solid; padding: 0 5px 0 5px;"
     " overflow: auto; white-space: pre; }"
;

//
// This function writes the title and map contents to the ostream in an
// HTML-encoded format (to make them easier on the eye).
//
template<typename OStreamT, typename MapT>
void format_map(OStreamT& os, MapT& m, const std::string& title)
{
  os<< "<div class=\"var_map\">"
         "<div class=\"var_map_title\">"
    <<       title
    <<   "</div>";

  if (m.empty())
    os<< "<div class=\"var_pair\">EMPTY</div>";
  else
    for (typename MapT::const_iterator i = m.begin(); i != m.end(); ++i)
    {
      os<< "<div class=\"var_pair\">"
             "<div class=\"var_name\">"
        <<       i->first
        <<   "</div>"
             "<div class=\"var_value\">"
        <<       i->second
        <<   "</div>"
           "</div>";
    }
  os<< "</div>";
}

std::size_t process_id()
{
#if defined(BOOST_WINDOWS)
  return _getpid();
#else
  return getpid();
#endif
}


/// This function accepts and handles a single request.
template<typename Request>
int handle_request(Request& req)
{
  boost::system::error_code ec;
  
  //
  // Load in the request data so we can access it easily.
  //
  req.load(ec, true); // The 'true' means read and parse STDIN (ie. POST) data.

  //
  // Construct a `response` object (makes writing/sending responses easier).
  //
  response resp;

  //
  // Responses in CGI programs require at least a 'Content-type' header. The
  // library provides helpers for several common headers:
  //
  resp<< content_type("text/html");
  
  // You can also stream text to a response. 
  // All of this just prints out the form 
  resp<< "<html>"
         "<head>"
           "<title>FastCGI Echo Example</title>"
           "<style type=\"text/css\">"
      <<       gCSS_text <<
           "</style>"
         "<head>"
         "<body>"
           "Request ID = " << req.id() << "<br />"
           "Process ID = " << process_id() << "<br />"
           "<form method=POST enctype='multipart/form-data'>"
             "<input type=text name=name value='"
      <<         req[post]["name"] << "' />"
             "<br />"
             "<input type=text name=hello value='"
      <<         req[post]["hello"] << "' />"
             "<br />"
             "<input type=file name=user_file />"
             "<input type=hidden name=cmd value=multipart_test />"
             "<br />"
             "<input type=submit value=submit />"
           "</form><p />";

  //
  // Use the function defined above to show some of the request data.
  // (this function isn't part of the library)
  //
  format_map(resp, req[env], "Environment Variables");
  format_map(resp, req[get], "GET Variables");
  format_map(resp, req[post], "POST Variables");
  format_map(resp, req[cookies], "Cookie Variables");

  // Print the complete buffer containing the POST data and the FastCGI params.
  resp<< "<pre>";
  BOOST_FOREACH(char& ch, req.post_buffer())
  {
    resp<< ch;
  }
  //    << req.get_buffer()
  resp<< "</pre>";

  //
  // Response headers can be added at any time before send/flushing it:
  //
  resp<< "Response content-length == "
      << resp.content_length() // the content-length (returns std::size_t)
      << content_length(resp); // a content-length header

  // This funky macro finishes up:
  return_(resp, req, 0);
  //
  // It is equivalent to the below, where the third argument is represented by
  // `program_status`:
  //
  // resp.send(req.client());
  // req.close(resp.status(), program_status);
  // return program_status;
  //
  // Note: in this case `program_status == 0`.
  //
}

int main()
{
try {

  // Make a `service` (more about this in other examples).
  service s;
  // Make an `acceptor` for accepting requests through.
  acceptor a(s);

  //
  // After the initial setup, we can enter a loop to handle one request at a
  // time until there's an error of some sort.
  //
  int ret(0);
  for (;;)
  {
    request req(s);
    //
    // Now we enter another loop that reuses the request's memory - makes
    // things more efficient). You should always do this for 
    // now; this requirement might be removed in future.
    //
    for (;;)
    {
      a.accept(req);
      ret = handle_request(req);
      if (ret)
        break;
      //
      // Clear the request's data, so information doesn't pass between
      // different users (this step isn't really necessary, because
      // the library will do this automatically.
      //
      req.clear();
    }
  }
  
  return ret;

}catch(boost::system::system_error& se){
  // This is the type of error thrown by the library.
  cerr<< "[fcgi] System error: " << se.what() << endl;
  return 1313;
}catch(exception* e){
  // Catch any other exceptions
  cerr<< "[fcgi] Exception: " << e->what() << endl;
  return 666;
}catch(...){
  cerr<< "[fcgi] Uncaught exception!" << endl;
  return 667;
}
}

See the full source listing.

This example uses Google.cTemplate to move HTML out of the program and into HTML template files, which would be easily editable by anyone familiar with HTML.

[Note] Note

You need to download and install Google's cTemplate library and you may have to modify Jamfile.v2 to point to your compiled ctemplate library (see the lib ctemplate ... lines).

//
// Amortization Calculator
// -----------------------
//
// This file uses Google cTemplate to show the benefits of using an
// HTML template engine. The code isn't commented yet, but should be 
// *reasonably* self-explanatory.
//
// It is a very basic amortization calculator.
//
#include <iostream>
#include <iomanip>
#include <boost/cgi/fcgi.hpp>
#include <boost/algorithm/string/regex.hpp>
#include <google/template.h>

using namespace boost::fcgi;

/// Convert a string like '$250,000' into one like '250000'.
std::string string_from_currency(std::string amt)
{
  // this is much too hardcore, but it works fine...
  boost::algorithm::erase_all_regex(amt, boost::regex("[$, ]"));
  return amt;
}

/// This function fills the dictionary and sub-dictionaries with relevant values.
template<typename Request>
void fill_amortization_dictionary(google::TemplateDictionary& dict, Request& req)
{
  std::string tmp( req.POST("LoanAmt") );
  dict.SetValue("LoanAmt", tmp.empty() ? "$250,000" : tmp);

  tmp = req.POST("YearlyIntRate");
  dict.SetValue("YearlyIntRate", tmp.empty() ? "6.000" : tmp);

  boost::array<std::string, 8> year_opts
    = {{ "5", "7", "10", "20", "30", "40", "50" }};
    
  BOOST_FOREACH(std::string& year, year_opts)
  {
    dict.SetValueAndShowSection("TermYrs", year, "SELECT_TERM_YEARS");
  }

  if (req.POST("Amortize").empty())
    dict.ShowSection("NotAmortize");
  else
  {
    double P = boost::lexical_cast<double>(string_from_currency(req.POST("LoanAmt")));
    double i = boost::lexical_cast<double>(req.POST("YearlyIntRate")) / 1200;
    double n = boost::lexical_cast<double>(req.POST("TermYrs")) * 12;
    double monthly_payments = (P*i) / (1 - std::pow((1+i), -n));
    
    google::TemplateDictionary* sub_dict = dict.AddSectionDictionary("RegPmtSummary");
    sub_dict->ShowSection("RegPmtSummary");
    sub_dict->SetFormattedValue("MonthlyPmt", "%.2f", monthly_payments);

    dict.ShowSection("Amortize");
    dict.SetValue("SCRIPT_NAME", req.script_name());

    double balance = P;
    int row_num = 0;
    double interest;
    double principal_paid;
    double total_interest = 0;
    do{
      google::TemplateDictionary* sub_dict2 = dict.AddSectionDictionary("PaymentEntry");
      sub_dict2->ShowSection("PaymentEntry");
      sub_dict2->SetFormattedValue("Payment", "%.2f", monthly_payments);
      sub_dict2->SetIntValue("ROW_NUM", ++row_num);
      sub_dict2->SetIntValue("ROW_TYPE", (row_num % 2) + 1);
      interest = balance * i;
      total_interest += interest;
      sub_dict2->SetFormattedValue("InterestPaid", "%.2f", interest);
      principal_paid = monthly_payments - interest;
      sub_dict2->SetFormattedValue("PrincipalPaid", "%.2f", principal_paid);
      balance -= principal_paid; // Note: balance can increase.
      sub_dict2->SetFormattedValue("Balance", "%.2f", balance);

    }while(balance > 0);

    sub_dict->SetFormattedValue("RegPmt_TotalIntPd", "%.2f", total_interest);
    sub_dict->SetFormattedValue("RegPmt_TotalPmts", "%.2f", total_interest + P);
  }
}

template<typename Request>
int write_amortization_template(Request& req, response& resp)
{
  google::TemplateDictionary dict("amortization");

  fill_amortization_dictionary(dict, req);

  google::Template* tmpl
    = google::Template::GetTemplate("amortization.tpl", google::STRIP_WHITESPACE);

  std::string h("Content-type: text/html\r\n\r\n");
  write(req.client(), buffer(h));

  std::string arg(req.GET("arg"));
  if (arg.empty())
    arg = "2"; // set this as default (for no particular reason).

  // Different, but equivalent ways of writing the output.
  if (arg == "1")
  {
    std::string output;
    tmpl->Expand(&output, &dict);
    resp<< output;
  }else
  if (arg == "2")
  {
    std::string output;
    tmpl->Expand(&output, &dict);
    write(req.client(), buffer(output));
  }else
//  if (arg == "3")
//  {
//    // This requires a modified version of Google.cTemplate, so it won't work.
//    std::string s;
//    std::vector<boost::asio::const_buffer> out;
//
//    tmpl->Expand(&s, &out, &dict);
//    write(req.client(), out);
//  }else
  {
    resp<< "Error!";
    return 1;
  }

  return 0;
}

int handle_request(acceptor& a)
{
  boost::system::error_code ec;

  request req(a.protocol_service());
 
  int ret = 0;
  int num = 0;
  while(!ec && ret == 0)
  {
    response resp;
    ++num;

    // Accepting on a closed request is fine (and more efficient than constantly
    // creating/destructing request objects). You must call close() first though!
    a.accept(req);

    req.load(true);

    resp<< content_type("text/html")
        << "map size := " << req.POST().size() << "<p>";
  
    ret = write_amortization_template(req, resp);

    resp.send(req.client(), ec);

    ret = ret ? ret : req.close(resp.status(), 0,  ec);
  }
  return ret;
}

void accept_requests(acceptor& a)
{
  for(;;)
  {
    // Keep handling requests until something goes wrong.
    if (handle_request(a))
      break;
  }
}

int main()
{
  try{

    service s;
    acceptor a(s, true); // The true means default-initialise.
                         // Unfortunately this only works on linux w. apache for now.

    accept_requests(a);
    
    return 0;

  }catch(boost::system::error_code& err){
    std::cerr<< "CGI error(" << err.value() << "): " << err.message() << std::endl;
  }catch(boost::system::system_error& err){
    std::cerr<< "System error(" << err.code() << "): " << err.what() << std::endl;
  }catch(...){
    std::cerr<< "ERROR!! BOOM!" << std::endl;
  }
}

See the full source listing.

This is a basic example demonstrating how using a Server can make your programs less dependent on a specific protocol.

//
// This example simply echoes all variables back to the user. ie.
// the environment and the parsed GET, POST and cookie variables.
// Note that GET and cookie variables come from the environment
// variables QUERY_STRING and HTTP_COOKIE respectively.
//
// It is a demonstration of how a 'server' can be used to abstract
// away the differences between FastCGI and CGI requests.
//
// This is very similar to the fcgi_echo example.
//

#include "boost/cgi/fcgi.hpp"
#include "./server.hpp"

using namespace std;
using namespace boost::fcgi;

// This function writes the title and map contents to the ostream in an
// HTML-encoded format (to make them easier on the eye).
template<typename OStream, typename Map>
void format_map(OStream& os, Map& m, const std::string& title)
{
  os<< "<h2>" << title << "</h2>";
  if (m.empty()) os<< "NONE<br />";
  for (typename Map::const_iterator i = m.begin(), end = m.end()
      ; i != end; ++i)
  {
    os<< "<b>" << i->first << "</b> = <i>" << i->second << "</i><br />";
  }
}

/// The handle_request function handles a single request.
int handle_request(request& req, boost::system::error_code& ec)
{
  // Construct a `response` object (makes writing/sending responses easier).
  response resp;

  // Responses in CGI programs require at least a 'Content-type' header. The
  // library provides helpers for several common headers:
  resp<< content_type("text/html")
      // You can also stream text to a response object.
      << "Hello there, universe!<p />";

  // Use the function defined above to show some of the request data.
  format_map(resp, req[env_data],    "Environment Variables");
  format_map(resp, req[get_data],    "GET Variables");
  format_map(resp, req[cookie_data], "Cookie Variables");
   // Response headers can be added at any time before send/flushing it:
  resp<< "<h3>Response Length</h3>" << resp.content_length()
      // response::content_length() returns the length of the *body*
      // of the response (ie. not including the headers).
      << content_length(resp);

  // This funky macro finishes up:
  return_(resp, req, 0);
  // It is equivalent to the below, where the third argument is represented by
  // `program_status`:
  //
  // resp.send(req.client());
  // req.close(resp.status(), program_status);
  // return program_status;
  //
  // Note: in this case `program_status == 0`.
}

///////////////////////////////////////////////////////////
int main()
///////////////////////////////////////////////////////////
try
{
  server4 s(&handle_request);

  return s.run();

}
catch(boost::system::system_error& se){
  cerr<< "[fcgi] System error (" << se.code() << "): "
      << se.what() << endl;
  return 1313;
}
catch(exception& e){
  cerr<< "[fcgi] Exception: " << e.what() << endl;
  return 666;
}
catch(...){
  cerr<< "[fcgi] Unknown exception!" << endl;
  return 667;
}

This is a very slight step up from the Server 1 example. It accepts new requests asynchronously, loads them synchronously and then handles them in a background thread while repeating the accept cycle.

The asynchronous functions are run in ten background threads (see server::run).

//
// This example simply echoes all variables back to the user. ie.
// the environment and the parsed GET, POST and cookie variables.
// Note that GET and cookie variables come from the environment
// variables QUERY_STRING and HTTP_COOKIE respectively.
//
// It is a demonstration of how a 'server' can be used to abstract
// away the differences between FastCGI and CGI requests.
//
// This is very similar to the fcgi_echo and fcgi_server1 examples.
// Unlike in the server1 example, the server class in this example uses
// asynchronous functions, to increase throughput.
//

#include <fstream>
///////////////////////////////////////////////////////////
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/program_options/environment_iterator.hpp>
///////////////////////////////////////////////////////////
#include "boost/cgi/fcgi.hpp"

using namespace std;
using namespace boost;
using namespace boost::fcgi;


// This function writes the title and map contents to the ostream in an
// HTML-encoded format (to make them easier on the eye).
template<typename OStream, typename Map>
void format_map(OStream& os, Map& m, const std::string& title)
{
  os<< "<h2>" << title << "</h2>";
  if (m.empty()) os<< "NONE<br />";
  for (typename Map::const_iterator i = m.begin(), end = m.end()
      ; i != end; ++i)
  {
    os<< "<b>" << i->first << "</b> = <i>" << i->second << "</i><br />";
  }
}

/// Handle one request and return.
/**
 * If it returns != 0 then an error has occurred. Sets ec to the error_code
 * corresponding to the error.
 */
int handle_request(fcgi::request& req, boost::system::error_code& ec)
{
  // Construct a `response` object (makes writing/sending responses easier).
  response resp;

  // Responses in CGI programs require at least a 'Content-type' header. The
  // library provides helpers for several common headers:
  resp<< content_type("text/html")
  // You can also stream text to a response object. 
      << "Hello there, universe!<p />";

  // Use the function defined above to show some of the request data.
  format_map(resp, req[env_data], "Environment Variables");
  format_map(resp, req[get_data], "GET Variables");
  format_map(resp, req[cookie_data], "Cookie Variables");

  //log_<< "Handled request, handling another." << std::endl;

  // This funky macro finishes up:
  return_(resp, req, 0);
  // It is equivalent to the below, where the third argument is represented by
  // `program_status`:
  //
  // resp.send(req.client());
  // req.close(resp.status(), program_status);
  // return program_status;
  //
  // Note: in this case `program_status == 0`.
}


/// The server is used to abstract away protocol-specific setup of requests.
/**
 * This server only works with FastCGI, but as you can see in the
 * request_handler::handle_request() function above, the request in there could
 * just as easily be a cgi::request.
 *
 * Later examples will demonstrate making protocol-independent servers.
 * (**FIXME**)
 */
class server
{
public:
  typedef fcgi::service                         service_type;
  typedef fcgi::acceptor                        acceptor_type;
  typedef fcgi::request                         request_type;
  typedef boost::function<
            int ( request_type&
                , boost::system::error_code&)
          >                                     function_type;

  server(const function_type& handler)
    : handler_(handler)
    , service_()
    , acceptor_(service_)
  {}

  void run(int num_threads = 0)
  {
    // Create a new request (on the heap - uses boost::shared_ptr<>).
    request_type::pointer new_request = request_type::create(service_);
    // Add the request to the set of existing requests.
    requests_.insert(new_request);

    start_accept(new_request);
    boost::thread_group tgroup;
    for(int i(num_threads); i != 0; --i)
    {
      tgroup.create_thread(boost::bind(&service_type::run, &service_));
    }
    tgroup.join_all();
  }

  void start_accept(request_type::pointer& new_request)
  {
    acceptor_.async_accept(*new_request, boost::bind(&server::handle_accept
                                                    , this, new_request
                                                    , boost::asio::placeholders::error)
                          );
  }

  void handle_accept(request_type::pointer req  
                    , boost::system::error_code ec)
  {
    if (ec)
    {
      //std::cerr<< "Error accepting request: " << ec.message() << std::endl;
      return;
    }

    req->load(ec, true);

    //req->async_load(boost::bind(&server::handle_request, this
    //                           , req, boost::asio::placeholders::error)
    //               , true);

    service_.post(boost::bind(&server::handle_request, this, req, ec));

    // Create a new request (on the heap - uses boost::shared_ptr<>).
    request_type::pointer new_request = request_type::create(service_);
    // Add the request to the set of existing requests.
    requests_.insert(new_request);

    start_accept(new_request);
  }

  void handle_request(request_type::pointer req
                     , boost::system::error_code ec)
  {
    handler_(*req, ec);
    if (ec)
    {
      //std::cerr<< "Request handled, but ended in error: " << ec.message()
      //         << std::endl;
    }
    start_accept(req);
  }

private:
  function_type handler_;
  service_type service_;
  acceptor_type acceptor_;
  std::set<request_type::pointer> requests_;
};

int main()
try
{
  server s(&handle_request);

  // Run the server with 10 threads handling the asynchronous functions.
  s.run(10);

  return 0;
  
}catch(boost::system::system_error& se){
  cerr<< "[fcgi] System error: " << se.what() << endl;
  return 1313;
}catch(std::exception* e){
  cerr<< "[fcgi] Exception: " << e->what() << endl;
  return 666;
}catch(...){
  cerr<< "[fcgi] Uncaught exception!" << endl;
  return 667;
}

This is essentially the same as the Server 1 example, but it runs handles mutiple requests simultaneously by calling server::run in a number of threads.

//
// This example simply echoes all variables back to the user. ie.
// the environment and the parsed GET, POST and cookie variables.
// Note that GET and cookie variables come from the environment
// variables QUERY_STRING and HTTP_COOKIE respectively.
//
// It is a demonstration of how a 'server' can be used to abstract
// away the differences between FastCGI and CGI requests.
//
// This is very similar to the fcgi_echo example.
//

#include <fstream>
///////////////////////////////////////////////////////////
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/program_options/environment_iterator.hpp>
///////////////////////////////////////////////////////////
#include "boost/cgi/fcgi.hpp"

using namespace std;
using namespace boost;
using namespace boost::fcgi;

/// Handle one request and return.
/**
 * If it returns != 0 then an error has occurred. Sets ec to the error_code
 * corresponding to the error.
 */
int handle_request(fcgi::request& req, boost::system::error_code& ec)
  {
    // Construct a `response` object (makes writing/sending responses easier).
    response resp;

    // Responses in CGI programs require at least a 'Content-type' header. The
    // library provides helpers for several common headers:
    resp<< content_type("text/plain")
    // You can also stream text to a response object. 
        << "Hello there, universe!";

    //log_<< "Handled request, handling another." << std::endl;

    // This funky macro finishes up:
    return_(resp, req, 0);
    // It is equivalent to the below, where the third argument is represented by
    // `program_status`:
    //
    // resp.send(req.client());
    // req.close(resp.status(), program_status);
    // return program_status;
    //
    // Note: in this case `program_status == 0`.
  }

/// The server is used to abstract away protocol-specific setup of requests.
/**
 * This server only works with FastCGI, but as you can see in the
 * request_handler::handle_request() function above, the request in there could
 * just as easily be a cgi::request.
 *
 * Later examples will demonstrate making protocol-independent servers.
 * (**FIXME**)
 */
class server
{
public:
  typedef fcgi::request                         request_type;
  typedef boost::function<
            int ( request_type&
                , boost::system::error_code&)
          >                                     function_type;

  server(const function_type& handler)
    : handler_(handler)
    , service_()
    , acceptor_(service_)
  {}

  int run()
  {
    // Create a new request (on the heap - uses boost::shared_ptr<>).
    request_type::pointer new_request = request_type::create(service_);
    // Add the request to the set of existing requests.
    requests_.insert(new_request);
    
    int ret(0);
    for (;;)
    {
      boost::system::error_code ec;

      acceptor_.accept(*new_request, ec);

      if (ec) 
      {
        std::cerr<< "Error accepting: " << ec.message() << std::endl;
        return 5;
      }
  
      // Load in the request data so we can access it easily.
      new_request->load(ec, true); // The 'true' means read and parse POST data.

      ret = handler_(*new_request, ec);

      if (ret)
        break;
    }
    return ret;
  }

private:
  function_type handler_;
  fcgi::service service_;
  fcgi::acceptor acceptor_;
  std::set<request_type::pointer> requests_;
};

int main()
try
{
  server s(&handle_request);

  // Run the server in ten threads.
  // => Handle ten requests simultaneously
  boost::thread_group tgroup;
  for(int i(10); i != 0; --i)
  {
    tgroup.create_thread(boost::bind(&server::run, &s));
  }
  tgroup.join_all();
  
}catch(boost::system::system_error& se){
  cerr<< "[fcgi] System error: " << se.what() << endl;
  return 1313;
}catch(std::exception* e){
  cerr<< "[fcgi] Exception: " << e->what() << endl;
  return 666;
}catch(...){
  cerr<< "[fcgi] Uncaught exception!" << endl;
  return 667;
}

[Note] Note

The key bit of this example is making an fcgi::service and fcgi::acceptor and then using the acceptor to check if the program is running in FastCGI or CGI mode - by calling fcgi::acceptor::is_cgi() - then you handle it accordingly.

//
// A completely stripped-down, protocol-independent program, outputs only
// "Hello there, universe."
//
// example/xcgi/basic$ `bjam install`
// 
// will install the example to your cgi-bin and fcgi-bin. Look
// [link ../../doc.qbk here] for more information, including how to set these.
//

#include <boost/cgi.hpp>
#include <boost/cgi/fcgi.hpp>

using namespace std;
using namespace boost::fcgi;

template<typename Request, typename Response>
int handle_request(Request& req, Response& resp)
{
  // This is a minimal response. The content_type(...) may go before or after
  // the response text.
  resp<< content_type("text/plain")
      << "Hello there, universe.";

  return_(resp, req, 0);
}

/// Handle a vanilla CGI request
int handle_request()
{
  boost::cgi::request req;
  response resp;
  return handle_request(req,resp);
}

/// Handle a FastCGI request
template<typename Acceptor>
int handle_request(Acceptor& a)
{
  request req(a.protocol_service());   // Our request.

  int ret = 0;
  for (;;) // Handle requests until something goes wrong
           // (an exception will be thrown).
  {
    a.accept(req);
    response resp;    // A response object to make our lives easier.
    ret = handle_request(req, resp);
  }

  return ret;
}

int main()
{
try
{
  service s;        // This becomes useful with async operations.
  acceptor a(s);

  return a.is_cgi() ?
             handle_request()    // Start it as a CGI request.
           : handle_request(a);  // Start it as a FastCGI request.
}
catch(boost::system::system_error& err)
{
  std::cerr<< "System error " << err.code() << ": "
           << err.what() << std::endl;
  return 1;
}
catch(...)
{
  std::cerr<< "Unknown error!" << std::endl;
  return 2;
}
}

See the full source listing.

First, the Server class, which can dispatch both CGI and FastCGI requests. The handler passed to Server::run() must be able to deal with both boost::acgi::request and boost::fcgi::request types. An example handler is in main.cpp (shown below), which deals with all types.

#include <boost/cgi.hpp>
#include <boost/cgi/fcgi.hpp>

class Server
{
public:
  Server()
    : service_()
    , acceptor_(service_)
  {
  }

  template<typename Handler>
  int run(Handler handler = Handler())
  {
    return acceptor_.is_cgi() ?
               handle_cgi_request(handler)
             : handle_fcgi_requests(handler);
  }

  template<typename Handler>
  int handle_cgi_request(Handler handler)
  {
    // **FIXME**
    // Uses acgi - would be better if boost::cgi::request was asynchronous.
    boost::acgi::service srv;
    boost::acgi::request req(srv);
    boost::acgi::response resp;
    return handler(req, resp);
  }

  template<typename Handler>
  int handle_fcgi_requests(Handler handler)
  {
    boost::fcgi::request req(service_);

    int ret = 0;
    for (;;) // Handle requests until something goes wrong
             // (an exception will be thrown).
    {
      acceptor_.accept(req);
      boost::fcgi::response resp;
      ret = handler(req, resp);
    }
    return ret;
  }
private:  
  boost::fcgi::service service_;
  boost::fcgi::acceptor acceptor_;
};

The request_handler is the important part of the example below, the part you should supply.

//
// A protocol-independent program, outputs only
// 
//   "Hello there, universe." 
//
// This one is similar to the xcgi/basic example and even more straigh-forward
// simple. The difference here is that the protocol-dependent bits are done by
// `Server` class (see "Server.hpp"). All the Server object takes is a handler
// that is a function object with a compatible signature.
//
//   example/xcgi/server1$ `bjam install`
// 
// will install the example to your cgi-bin and fcgi-bin. Look
// [link ../../doc.qbk here] for more information, including how to set these.
//

#include "Server.hpp"

using namespace std;
using namespace boost::cgi::common;

// Our request handler 
struct request_handler
{
  // Handle any request type.
  template<typename Request, typename Response>
  int operator()(Request& req, Response& resp)
  {
    // This is a minimal response. The content_type(...) may go before or after
    // the response text.
    resp<< content_type("text/plain")
        << "Hello there, universe.";
 
    return_(resp, req, 0);
  }
};

int main()
{
try
{
  Server server;
  return server.run(request_handler());
  //return server.run<request_handler>();      // Equivalent to the line above.
}
catch(boost::system::system_error& err)
{
  std::cerr<< "System error " << err.code() << ": "
           << err.what() << std::endl;
  return 1;
}
catch(...)
{
  std::cerr<< "Unknown error!" << std::endl;
  return 2;
}
}

See the full source listing:


PrevUpHomeNext