How to build a custom static file serving HTTP server using Libevent in C

How to build a custom static file serving HTTP server using Libevent in C

Libevent is an event notification library which lays the foundation for immensely successful open source projects like Memcached. As the web advances into a real time mode, more and more websites are using a mix of technologies like HTTP Pub-Sub, HTTP Long-polling and Comet with a custom light weight HTTP servers in the backend to create a real time user experience. In this blog post, I will start with necessary prerequisites for setting up the development environment. Further, I will demonstrate how to build a HTTP server capable of serving static pages. Finally, I will put up a few use cases of a custom HTTP server in today’s world.

Setting up Environment
Follow the following steps to install the latest version of libevent (version 2.0.3-alpha)

  • $ wget
  • $ tar -xvzf libevent-2.0.3-alpha.tar.gz
  • $ cd libevent-2.0.3-alpha.tar.gz
  • ./configure
  • make
  • sudo make install

Check the environment by running the following piece of C code (event2.cpp):

#include <event2/event.h>

int main(int argc, char **argv) {
	const char *version;
	version = event_get_version();
	printf("%sn", version);
	return 0;

Compile and run as following:

$ g++ -arch x86_64 -Wall -levent event2.cpp -o event2
$ ./event2
$ 2.0.3-alpha

I had to pass -arch x86_64 flags on Mac OSX 10.5.8. This can vary depending upon your operating system.

Libsrvr: Static file serving HTTP Server
Below is the C code for a static file serving HTTP server using libevent called “Libsrvr”:


// General purpose header files
#include <iostream>
#include <getopt.h>
#include <sys/stat.h>

// Libevent header files
#include </usr/local/include/event2/event.h>
#include </usr/local/include/event2/http.h>
#include </usr/local/include/event2/buffer.h>

// Libsrvr configuration settings
#define LIBSRVR_SIGNATURE "libsrvr v 0.0.1"
#define LIBSRVR_HTDOCS "/Users/sabhinav/libsrvr/www"
#define LIBSRVR_INDEX "/index.html"

// Libsrvr http server and base struct
struct evhttp *libsrvr;
struct event_base *libbase;

// Libsrvr options
struct _options {
	int port;
	char *address;
	int verbose;
} options;
  • LIBSRVR_SIGNATURE is the server signature sent as response header for all incoming requests
  • LIBSRVR_HTDOCS is the path to the the DocumentRoot for libsrvr
  • LIBSRVR_INDEX is the similar to DirectoryIndex directive of apache


#include </Users/sabhinav/libsrvr/libsrvr.h>

void router(struct evhttp_request *r, void *arg) {
	const char *uri = evhttp_request_get_uri(r);

	char *static_file = (char *) malloc(strlen(LIBSRVR_HTDOCS) + strlen(uri) + strlen(LIBSRVR_INDEX) + 1);
	stpcpy(stpcpy(static_file, LIBSRVR_HTDOCS), uri);

	bool file_exists = true;
	struct stat st;
	if(stat(static_file, &st) == -1) {
		file_exists = false;
		evhttp_send_error(r, HTTP_NOTFOUND, "NOTFOUND");
	else {
		if(S_ISDIR(st.st_mode)) {
			strcat(static_file, LIBSRVR_INDEX);

			if(stat(static_file, &st) == -1) {
				file_exists = false;
				evhttp_send_error(r, HTTP_NOTFOUND, "NOTFOUND");

	if(file_exists) {
		int file_size = st.st_size;

		char *html;
		html = (char *) alloca(file_size);

		if(file_size != 0) {
			FILE *fp = fopen(static_file, "r");
			fread(html, 1, file_size, fp);

		struct evbuffer *buffer;
		buffer = evbuffer_new();

		struct evkeyvalq *headers = evhttp_request_get_output_headers(r);
		evhttp_add_header(headers, "Content-Type", "text/html; charset=UTF-8");
		evhttp_add_header(headers, "Server", LIBSRVR_SIGNATURE);

		evbuffer_add_printf(buffer, "%s", html);
		evhttp_send_reply(r, HTTP_OK, "OK", buffer);

		if(options.verbose) fprintf(stderr, "%st%dn", static_file, file_size);
	else {
		if(options.verbose) fprintf(stderr, "%st%sn", static_file, "404 Not Found");


int main(int argc, char **argv) {
	int opt;

	options.port = 4080;
	options.address = "";
	options.verbose = 0;

	while((opt = getopt(argc,argv,"p:vh")) != -1) {
		switch(opt) {
			case 'p':
				options.port = atoi(optarg);
			case 'v':
				options.verbose = 1;
			case 'h':
				printf("Usage: ./libsrvr -p port -v[erbose] -h[elp]n");

	libbase = event_base_new();
	libsrvr = evhttp_new(libbase);
	evhttp_bind_socket(libsrvr, options.address, options.port);
	evhttp_set_gencb(libsrvr, router, NULL);

	return 0;

Here is some explanation for the above code:

  • Command line options are parsed using GNU getopt library
  • libbase is the event base for HTTP server libsrvr.
  • HTTP server is bind to port 4080 (by default).
  • A callback function is registered for each incoming HTTP request to libsrvr. Function router is invoked every time a HTTP request is received
  • Finally libbase is dispatched and code never reaches return 0

The working of the router function is as follows:

  • Incoming request uri is converted to absolute file path on the system
  • Checks for file or directory existence is done
  • If absolute path is a directory, LIBSRVR_INDEX is served out of that directory

Launching Libsrvr:
Compile and run the libsrvr as follows:

$ g++ -arch x86_64 -Wall -levent libsrvr.cpp -o libsrvr
$ ./libsrvr -v
/Users/sabhinav/libsrvr/www//index.html	538
/Users/sabhinav/libsrvr/www/assets/style.css	35
/Users/sabhinav/libsrvr/www/assets/script.js	27
/Users/sabhinav/libsrvr/www/dummy	404 Not Found
/Users/sabhinav/libsrvr/www/index.html	538
/Users/sabhinav/libsrvr/www/assets/style.css	35

If started under verbose mode (-v), libsrvr will output each requested file path on the console as shown above.

Use cases
Below are a few use cases of a custom HTTP server as seen in web today:

  • Facebook Chat: Uses a custom http server based on mochiweb framework
  • Yahoo finance: Uses a custom http streaming server based on libevent

Generally, iframe technique is combined with javascript hacks for streaming data from the custom http servers. Read “How to make cross-sub-domain ajax (XHR) requests using mod_proxy and iframes” for details.

Though a static file server find little place in today’s world, the idea was to show the ease by which you can create your own HTTP server which is light weight, fast and scalable (all thanks to Niels for his libevent). Couple libsrvr with memcached for caching static files, and benchmark will show over 10,000 req/sec handling capability of libsrvr.

Share if you like it and also let me know your thoughts through comments.


  1. Anton

    It’s simple but cannot provide reasonable load service

    The code:

    FILE *fp = fopen(static_file, “r”);
    fread(html, 1, file_size, fp);

    It will work very slow if the file is not completely cached in memory. It’s blocking code, it will pause libevent event loop.

  2. Pingback: libevent实现的高并发static file HTTP server | Min的技术分享 –

Leave a Reply