[Today’s random Sourcerer profile: https://sourcerer.io/falsehonesty]

Last time on The Adventures of Bare Metal Web Development, I covered the basic concepts of building a website in C++. Using just a few lines of code in any language it is possible to output content to the web server to be passed along to the user.

In the response to the article, the point was raised that CGI (Common Gateway Interface) is slow. And yes, it is. The exercise was just an exploration of the possible, not the inherent performance issues with CGI. In the article I touched on various ways to improve performance, but I’d like to get even closer to the hardware.

To do that, let’s extend Apache itself.

An Interactiveodular Apache Modular

Yep, interactiveodular is a thing.

As I discussed in my last article, Apache is an excellent general-purpose web server. Arguably its shiniest feature its extensive support for modification via modules. Using C, we can augment and extend its behavior in dramatic ways. Before FPM (FastCGI Process Manager), PHP used this approach to parse PHP files through its interpreter. If PHP were completely thread safe, this approach could still be used (and would likely be superior given mod_event’s performance).

Regardless, an Apache module is the fastest way to interact with a user via C code using the Apache web server.

A Note About Shared Hosting

This is such an important note I didn’t want to give it a dad-joke-esque heading.

You can’t use this approach with shared hosting.

While you could compile an Apache module in a shared hosting system (as long as you had compiler access), installing an Apache module requires root access (or at least access to the user that runs Apache, and on shared you won’t have that).

If you know your host and they trust you enough to install a module, you’d still run into issues because a handler could be used by any site, not just yours.

Bottom line: To use this approach, you need your own private instance of Apache. If you want to use C/C++ in that kind of environment, CGI will likely be your only only avenue.

The Right Tools for the Job

You’re going to need Linux. You could do it in Windows or macOS, but the tool chain setup and installation of the module itself is more complicated and that would distract from the scope and purpose of this article, so we’ll stick to Linux. That doesn’t mean you need a Linux server — you can install Debian or your favorite distribution in a virtual machine or on a USB thumb drive.

Since there are many different distributions, the exact package names will differ, but we need to install Apache and it’s development requirements. On Debian/Ubuntu/Linux Mint, this would be:

apt install build-essential apache2 apache2-dev

The only other thing you’ll need is a good text editor. I recommend Vim (apt install vim), but nano or other editors would work. In a pinch, I suppose you could use emacs.

The Bare Minimum Code

Here’s an example module that will showcase the bare essentials. We’ll dive into each part after this.

/*

* mod_hello.c - A simple Apache module

*/ #include "httpd.h"

#include "http_config.h"

#include "http_protocol.h"

#include "ap_config.h" /* Our "Hello, World" Handler */

static int hello_handler(request_rec *req) { if (strcmp(req->handler, "hello")) {

return DECLINED;

} /* Output our string via ap_rputs */

if (!req->header_only) {

ap_rputs("<h1>Hello, World!</h1>", req);

} /* Set the MIME type */

req->content_type = "text/html;charset=UTF-8"; apr_table_set(req->headers_out, "X-Content-Type-Options", "nosniff"); return OK;

} /* Register the hook */

static void hello_register_hooks(apr_pool_t *p) {

ap_hook_handler(hello_handler, NULL, NULL, APR_HOOK_MIDDLE);

} /* Dispatcher for API hooks */

module AP_MODULE_DECLARE_DATA hello_module = {

STANDARD20_MODULE_STUFF,

/* These hooks allow you to define and merge

* per-dir or per-server configuration structrures.

* We don't need them in this example because our

* module doesn't define any config parameters.

*/

NULL,

NULL,

NULL,

NULL,

NULL,

/* Hook for registration */

hello_register_hooks

};

In our above example, we include the httpd.h and associated headers, define a handler that outputs Hello, World in an H1 tag, registers the handler, then defines the required structure called AP_MODULE_DECLARE_DATA with a pointer to the hello_register_hooks function that registers the handler.

Practical Examples

Now that you have the basic template, you can expand on this to fulfill the required real-world functionality your website needs.

In the example above, we’re simply outputting text via ar_rputs. While this is a great way to send HTML to the user, a real website is going to need a lot more processing before simply outputting a string.

You could read HTML files from disk into a char buffer and then output them via ar_rputs, but this would be slightly slower than just referencing the HTML files directly via the server’s native file serving capabilities. Instead, while you’re in C, you can pull data via MySQL or PostgreSQL’s C interface, or even perform business logic to mix HTML with data from an SQL server.

Let’s get into some real world examples.

GET POSTing Along

But one of the most useful things in processing a request is being able to parse GET and POST variables. Here’s a code sample that could be added to the our code to do just that:

/* Be sure to add this to your includes */

#include "apr_strings.h" /* Define the key_value_pair structure */

typedef struct {

const char *key;

const char *value;

} key_value_pair; key_value_pair *read_post(request_rec *req) {

apr_array_header_t *pairs = NULL;

apr_off_t length;

apr_size_t size;

int res;

int i = 0;

char *buffer;

key_value_pair *kvp; /* Parse form data into res

HUGE_STRING_LEN is defined by the Apache Portable Runtime (APR) */

res = ap_parse_form_data(req, NULL, &pairs, -1, HUGE_STRING_LEN);



/* Return NULL if there's no POST data or something went wrong */

if (res != OK || !pairs) return NULL;



/* Allocate memory based on the size of the

key value pair structure times the number of pairs,

plus one for buffer */

kvp = apr_pcalloc(req->pool, sizeof(key_value_pair) * (pairs->nelts + 1));



/* Loop over the pairs and append to our structure kvp */

while (pairs && !apr_is_empty_array(pairs)) {

ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);

apr_brigade_length(pair->value, 1, &length);

size = (apr_size_t) length;

buffer = apr_palloc(req->pool, size + 1);

apr_brigade_flatten(pair->value, buffer, &size);

buffer[length] = 0;

kvp[i].key = apr_pstrdup(req->pool, pair->name);

kvp[i].value = buffer;

i++;

}

return kvp;

}

With this function defined, you can get the pairs in our handy structure:

key_value_pair *post_vars; /* Read POST variables from request (req) */

post_vars = read_post(req);



No HTML, Please — We Prefer JSON

Want to reply with JSON rather than HTML? Easy enough. Just change this line:

req->content_type = “text/html;charset=UTF-8”;

to read:

req->content_type = “application/json;charset=UTF-8”;

and change the ar_rputs line to output JSON.

This approach is incredibly useful when talking to other code or mobile applications.

Installing Your Shiny New Module

If you run:

sudo apxs -i -c mod_hello.c

Apache will try to install your module. If Apache runs as another user (or your user), you may not need to use sudo, but I’ve added it just in case.

To load the module, add:

LoadModule hello_module modules/mod_hello.so <Location /hello>

SetHandler hello

</Location>

To your httpd.conf file. On Debian, you can do so cleanly by adding it to the file /etc/apache2/conf-available/hello.conf then running:

sudo a2enconf hello

and restarting Apache via:

sudo service apache2 restart

If you navigate to:

(replace localhost with your Linux computer’s name)

you’ll see “Hello, World!”

Extending Our Reach

Now that you have the basics, you can create any kind of Apache module your heart desires.

For a deeper guide and API reference, see Developing Modules for the Apache HTTP Server. You’ll want to be sure to use APR-based functions to keep your module completely portable between operating systems.

I hope you’ve enjoyed this quick peek into the wild world of Apache modules. Happy extending!