The HTTP Server
The httpd class is a flexible web server component that allows you to set up parts of your application as a web service. It has a flexible component lay-out that lets you define those element of a web server that make sense for your particular application domain without involving any unnecessary baggage.
Using the Class
Using httpd in isolation, you will get a HTTP service that generates 404 errors on every URI you feed it. It sets up a listening number of worker threads in the background that respond to HTTP commands. Using the minthreads and maxthreads call you can control the thread allocation strategy.
#include <grace/httpd.h> #include <grace/daemon.h> #include "mydaemon.h" int mydaemon::main (void) { httpd server; daemonize(); server.listento (8888); server.minthreads (2); server.maxthreads (16); server.start (); log::write (log::info, "main", "Web service started"); while (true) { value ev = waitevent (); if (ev.type() == "shutdown") break; } log::write (log::info, "main", "Shutting down"); server.shutdown (); stoplog (); return 0; }
Attaching Handlers
If all you're after is a HTTP service that sends 404 replies on every request, you can stop reading now. To get more useful behavior from the httpd class, you can add objects derived from the httpdobject class to handle requests.
A httpdobject defines a URI path that it is interested in. It gets called whenever a request comes in that matches this path. It can either handle the request, leading to a HTTP reply, or stick to only processing the environment, leaving page output to a httpdobject further down the chain.
Let's take a look at this idea in action and implement a 'page':
#include <grace/httpd.h> #include <grace/daemon.h> #include "mydaemon.h" class httphello : public httpdobject { public: httphello (httpd &server) : httpdobject (server, "/") { } ~httphello (void) {} int run (string &uri, string &postbody, value &inhdr, string &out, value &outhdr, value &env, tcpsocket &s); }; int httphello::run (string &uri, string &postbody, value &inhdr, string &out, value &outhdr, value &env, tcpsocket &s) { out = "<html><body>Hello, world</body></html>"; outhdr["Content-type"] = "text/html"; return 200; } int mydaemon::main (void) { httpd server; httphello hello (server); daemonize(); server.listento (8888); server.minthreads (2); server.maxthreads (16); server.start (); // ...
Here we used a buffered approach towards building the page. The httphello::run method puts content in the output buffer, adds a Content-type header and, in its return, indicates that httpd should wrap it up in a HTTP 200 status reply.
There may be situations where buffering the entire page is impractical — there may be just too much data or perhaps you are generating a stream. In that case, you are responsible for handling the entire HTTP reply cycle:
int httphello::run (string &uri, string &postbody, value &inhdr, string &out, value &outhdr, value &env, tcpsocket &s) { s.puts ("HTTP/1.1 200 OK\r\n" "Content-type: text/plain\r\n" "Connection: close\r\n\r\n"); s.writeln ("Hello, world"); s.close (); return -200; }
The -200 return is a bookkeeping affair here, if there is logging attached to the httpd object, it can keep track of the return status.
Chaining
If you add multiple httpdobject-derived instances matching the same URI, httpd will treat them as a chain that will be followed until it gets something other than a 0-return from a methodcall to run. You can use this to your advantage if you want to break up some generic request processing from the implementation of a more specific URI:
class httpacl : public httpdobject { public: httpacl (httpd &ht) : httpdobject (ht, "*") {} ~httpacl (void) {} int run (string &uri, string &postbody, value &inhdr, string &out, value &outhdr, value &env, tcpsocket &s) { if (env["ip"] == "127.0.0.1") return 0; outhdr["Content-type"] = "text/html"; out = "<html><body>NO!</body></html>"; return 401; } }; int mydaemon::main (void) { httpd server; httpacl acl (server); httphello hello (server); // ...
The new class looks at the "ip" environment-variable that is set by httpd. Through chaining, you can add more variables to the environment, but by default you will get the following:
| Variable | Type | Description |
|---|---|---|
| env["keepalive"] | boolean | If set to true, the client protocol negotiated a preference for keeping the connection alive. If this value is still set to true after the chain is finished in buffered mode, and the number of threads actively handling a connection is less than half of the available pool, the connection will be maintained. |
| env["method"] | string | The HTTP method sent (GET, POST, PUT, &c.) |
| env["ip"] | string | The remote ip address |
| env["referrer"] | string | The contents of the Referer-header, if any. |
Using httpdlogger
If you want to generate default Apache-style logfiles for your application, you can use the httpdlogger class. It's very easy to use:
int mydaemon::main (void) { httpd server; httpdlogger log (server, "log:myapp.access.log", "log:myapp.error.log", 1024 * 25); // ...
If you omit the parameter defining the error log, none will be created. The last argument is the maximum size a logfile is allowed to be before it is rotated, you can omit it to default to defaults::sz::logfile, which is 2 MB.
| Previous Chapter | Table of Contents | Next Chapter |