Add HTTP Services into C++ Programs Using PION-NET

In the recent four years, I have been working on parallel machine learning systems, where a learning job consists of multiple processes running in parallel on many computers.  These processes may run for a long time (from dozens of minutes up to several days), thus I often like to monitor their status like statistics (e.g., CPU and memory heap consumptions) and progress (how many data it has processed).  Logging can expose status, but usually not in a comprehensive way.  A more elegant solution is to make each process spawn a thread to run a Web service, which exposes the status in the format of HTML.

After a weeks-long survey, I decided to use pion-net, a library built upon Boost::Asio.  A blog post here explains some advantages of Boost::Asio. The building and installation of pion-net is straight-forward; just ./configure && make && make install. If you want to use your own builds of boost and openssl, use ./configure --with-openssl=openssl-prefix-dir --with-boost=boost-prefix-dir.

In the source directory /pion-net-3.0.15/net/utils/ of pion-net, there are examples showing how to use pion::net::TCPServer and pion::net::WebServer. Here follows an additional example for pion::net::HTTPServer:

// Copyright (C) 2010  Yi Wang (yi.wang.2005@gmail.com)
// Demonstrate how to add HTTP service into a Boost/C++ program.

#include <iostream>
#include <pion/net/HTTPServer.hpp>
#include <pion/net/HTTPTypes.hpp>
#include <pion/net/HTTPRequest.hpp>
#include <pion/net/HTTPResponseWriter.hpp>

using namespace std;
using namespace pion;
using namespace pion::net;

void handleRootURI(HTTPRequestPtr& http_request, TCPConnectionPtr& tcp_conn) {
  static const std::string kHTMLStart("<html><body>\n");
  static const std::string kHTMLEnd("</body></html>\n");

  HTTPResponseWriterPtr
    writer(HTTPResponseWriter::create(tcp_conn,
                                      *http_request,
                                      boost::bind(&TCPConnection::finish,
                                                  tcp_conn)));
  HTTPResponse& r = writer->getResponse();
  r.setStatusCode(HTTPTypes::RESPONSE_CODE_OK);
  r.setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_OK);

  HTTPTypes::QueryParams& params = http_request->getQueryParams();

  writer->writeNoCopy(kHTMLStart);
  writer->write("The query about resource ");
  writer->write(http_request->getResource());

  if (params.size() > 0) {
    writer->write(" has the following parameters: <br>");
    for (HTTPTypes::QueryParams::const_iterator i = params.begin();
         i != params.end(); ++i) {
      writer->write(i->first);
      writer->write("=");
      writer->write(i->second);
      writer->write("<br>");
    }
  } else {
    writer->write(" has no parameter.");
  }
  writer->writeNoCopy(kHTMLEnd);
  writer->send();
}

int main (int argc, char *argv[]) {
  static const unsigned int DEFAULT_PORT = 8080;
  try {
    HTTPServerPtr hello_server(new HTTPServer(DEFAULT_PORT));
    hello_server->addResource("/", &handleRootURI);
    hello_server->start();      // Spawn a thead to run the HTTP
                                // service, and the main thread can
                                // focus on the main work now.
    std::cout << "Hello, the server is running now ...\n";
    sleep(10);
  } catch (std::exception& e) {
    std::cerr << "Failed running the HTTP service due to " <<  e.what();
  }
  return 0;
}

The following Makefile lists libraries required to build this program:

pion_hello_service : pion_hello_service.cc
 g++ \
 -I/Users/wangyi/3rd-party/pion-net-3.0.15/include \
 -I/Users/wangyi/3rd-party/boost-1.43.0/include \
 -I/Users/wangyi/3rd-party/openssl-0.9.8o/include \
 pion_hello_service.cc \
 -L/Users/wangyi/3rd-party/pion-net-3.0.15/lib \
 -L/Users/wangyi/3rd-party/boost-1.43.0/lib \
 -L/Users/wangyi/3rd-party/openssl-0.9.8o/lib \
 -lpion-common -lpion-net \
 -lboost_thread -lboost_system -lboost_filesystem -lboost_regex \
 -lboost_date_time -lboost_signals -lboost_iostreams \
 -lssl -lcrypto -lz -lbz2 -ldl