April 18, 2024

Mini HTTP Daemon Service for RaspberryPi/Linux apps

So recently I had to design a relatively convoluted system with a database that communicates with a hardware controller board and a RFID reader. Among other things, the system has to respond to several commands issued from a web frontend over HTTP, and report the status of each sub-system, sensor, etc hopefully in JSON or similar web-friendly format.

For this task I picked a Raspberry Pi as the platform, and made a program in C that talks directly to the hardware and handles everything including the HTTP requests.  Now, this is definitely not the first time that I need to write a program that has to listen for requests and reply with simple data over HTTP,  so I thought that perhaps it would be useful to encapsulate this functionality in a small module that I could later re-use in other projects.

So I ended up doing exactly that, and uploaded the code to my GIT-Hub Repository, so you’ll find the result from that here: https://github.com/battlecoder/httpdpi.

Before you dive into the code, please bear in mind that it’s an extremely simple service that will only respond to GET requests, but has all it needs to reply with different status codes, text, and binary data. It only uses sockets and POSIX threads, so it’s very fast, doesn’t depend on a huge framework, and can run in parallel with your code. It’s also really easy to expand if you want to support other types of requests.

Since it doesn’t have obscure dependencies, it should also compile on most linux boxes including other small computers like C.H.I.P, Beaglebone, etc.

Basics

Please also note that the provided example is very basic and doesn’t perform fancy checks. This was done to keep things simple. Most of the sample replies lack proper HTML structure and you’ll need to add several extra safeguards in a real application. I’ll go over the example code here briefly, just focusing on the relevant parts.

The first step is defining 2 functions: One that will handle errors, and one that will handle successfully received requests. Their definition is as follows:

void HTTP_handler (int socket, struct sockaddr_in in_addr, char *requestURI) {
  /* Here goes the HTTP handling code */
}
void HTTP_errHandler (int socket, HTTPD_ERROR err){
  /* Here be the error handling code */
}

Each function receives the associated socket, which is required by the few handy functions from the module. However this also means you can handle everything at a low level yourself, using sockets, if you so desire.

Anyway, once you have those functions in your code, starting the HTTP service is done by calling:

httpd_start (8080, &HTTP_handler, &HTTP_errHandler)

In the example I’ve started the HTTP service on port 8080. HTTP traditionally runs in port 80, but 8080 is a popular choice if a traditional web service is already running. You can use any port you want, but with 80 you don’t need to specify the port in the URL as I’ll later need to do.

Of course there’s also httpd_stop(), which naturally stops the whole thing.

You’ll notice that HTTP_errHandler in the example only prints the received error code and its description, and does nothing else. In a real program you may want to stop the program execution if the server could not be started, or try a different port, etc, you know, actually reacting to the error messages.

Replying to Requests

The HTTP_handler function in the example implementation is also really basic. It compares the received request against several URIs it implements, and returns different data depending on what was requested. Normally nothing fancier is required, save for better responses and sanity checks. Of course extra parsing is needed if you want to receive arguments in the URL, or want to support paths with URL-encoded characters (I may add that later).

To reply to a request you can use the httpd_HTMLResponse function for standard HTML responses, and httpd_fileResponse to send binary data from a file. There’s a more generic method httpd_response that can be used to implement replies with data from other sources or with other MIME types.

Running the Example

Compile the example with $ make and run it with $ ./example.

Once running, the code will start increasing a counter each 5 seconds while the HTTP service runs, and your Raspberry Pi now will respond over HTTP to requests made to a few specific URLs, namely “/stop“, “/counter” and “/picture“. Anything else will cause a simple and generic 404 message.

Here you will see several calls made from my browser (Chrome) to my Raspberry Pi running the example. The local IP address of my Pi  in my home network is 192.168.2.103). I had to append the port to the URL as I wasn’t using the default for HTTP (80).

This is the console output of the program:

The same requests from the perspective of the Raspberry Pi.

After the “/stop” URI was invoked, the execution ended.

Since you can specify the MIME type of the data you return, you can serve pretty much any kind of content you want including JSON, XML, pictures, binary files, etc. And because you are running a program and not a PHP script, you can get this data from hardware sensors, input devices, system processes, weird filesystems and virtually anything you can access from native C code.

I know this will come in handy in my future projects, so that alone was reason enough to do this,  but it would be great if this happens to be useful to other people as well. Again, all the code and the example is in the GIT-Hub Repository if you want to give it a try.

Leave a Reply

Your email address will not be published. Required fields are marked *