PHP is without doubt a useful tool, however the language and specifically its API is somewhat inconsistent and lets be honest at times it can be a bottleneck. I find coding in C to be a less frustrating experience and if the language is good enough for the Apache developers that should tell you something. The example code I will present here is little more than a proof of concept, my aims were to keep it simple and at least prove I could handle a POST request.
In the past I've looked a few times at writing an Apache module, but finding information from the Apache website can be somewhat frustrating, there's a lot to document, and it is a somewhat niche activity (even for geeks!) so good (or any) tutorials are thin on the ground and not uncommonly out of date. That said once you're over the initial learning curve its no surprise that Apaches' support library (libapr) has some great functionality and does a great job at handling the minutia of Apache and the specifics of HTTP.
Its usual to use a utility called apxs to compile a module, however it puts all the build artifacts in the same directory as your source, the alternative is to have apxs create a skeleton project for you. Alas on my system the created project wouldn't compile and didn't exactly follow the KISS principle with multiple make includes (.mk files) which rarely goes well...
I like to have a specific layout for my projects, hiding build artifacts away in a hidden directory and splitting a project in to separate source units broadly relating to their use, especially for infrequently modified utility functions. It was a trivial matter to see what apxs was up to and replicate it with a Makefile that automagically takes all the source files in a directory and builds them into a module.
Apache has a simple data structure so it knows which functions to call in your module at the appropriate time.
static void post_example_hooks(apr_pool_t* pool) { ap_hook_handler(post_example_handler, NULL, NULL, APR_HOOK_MIDDLE) ; } module AP_MODULE_DECLARE_DATA post_example_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, NULL, post_example_hooks };
Well I looked all over for where this structure might be documented, but the best I could find was Apache module guide where a fragment of code is at least commented...
This is how apache knows where your handlers function is, the configuration for it is as you'd expect (in httpd.conf)
LoadModule post_example_module /usr/libexec/httpd/mod_post_example.so
<Location /postexample>
SetHandler post_example
</Location>
Once the code is compiled and installed, hitting the URL /postexample will trigger the module. The first thing that should happen in the actual handler function is to checked that it's been called for the "post_example" handler.
static int post_example_handler(request_rec* r) { if ( !r->handler || strcmp(r->handler, "post_example") ) { return DECLINED ; /* none of our business */ }
At this point we're all set to start generating content, the example will only deal with GET or POST requests, so after checking the request type I've bundled all the POST variables into a table. For convenience I've written this in a manner that won't fail even if the request is a GET request, but there will be times when you need to specifically check for a POST request and only do something if it is a POST, for example responding to a user input that relies on user supplied values. I shouldn't need to point out the importance of sanitizing POST data, but in this case as its a simple proof of concept and I'm not hitting a database (for example) all I'm doing is escaping html tags.
To actually create the table of POST variables I have a brief function in support.c
apr_table_t* populatePostVars(request_rec *r) { apr_table_t* tab; int res; apr_array_header_t *pairs = NULL; apr_off_t len; apr_size_t size; char *buffer; res = ap_parse_form_data(r, NULL, &pairs, -1, HUGE_STRING_LEN); if (res != OK || !pairs) return NULL; // Return NULL if we failed or if there are is no POST data tab = apr_table_make(r->pool, pairs->nelts +1); 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, &len); size = (apr_size_t) len; buffer = apr_palloc(r->pool, size + 1); apr_brigade_flatten(pair->value, buffer, &size); buffer[len] = 0; apr_table_set(tab, pair->name, buffer); } return tab; }
This takes an array from the request structure and populates a table, at a later date I want to be able to bundle form arrays together but that would needlessly complicate what I want to keep a simple example
Later on when actually outputting content we can just look up a variable name. For this I've written an even simpler function, the main purpose of which is to return an empty string instead of a NULL.
// an empty string to to use in place of a NULL static const char zstr[]="\0"; const char* GetPostVar(request_rec *r, apr_table_t* tab, const char* var) { const char* PostVar = apr_table_get(tab, var); if (!PostVar) { PostVar = zstr; // either its invalid var name or not even a POST request } else { PostVar = ap_escape_html(r->pool, PostVar); } return PostVar; }
When generating content its used like this
str = GetPostVar(r, tab, "input1"); ap_rprintf(r, "<input type=\"input\" name=\"input1\" value=\"%s\">%s<br>\n", str, str);
There is still lots I have to investigate about the APR library and the internals of Apache, but as hacks go I'm really happy with how it has come together, maybe after all writing Apache modules isn't as scary as it at first looks...
You can get the complete project here
Enjoy !