Update: I wrote a HOWTO guide on how to create an Apache HTTPd module years ago. This is an updated version of the same, that can be found in github.
Write your own httpd module could be interesting for a number of reasons:
- understand how a webserver works;
- understand Apache httpd;
- speed up your current system by optimizing a specific bottleneck;
- instrumentation;
- fun?
Warming up
So, let’s jump into it. First step is to clone the HTTPd code and build it.
$ git clone https://github.com/apache/httpd.git
$ cd httpd
$ git clone https://github.com/apache/apr.git srclib/apr
$ ./buildconf
$ mkdir mybuild
$ cd mybuild
$ CFLAGS="-O0 -ggdb" ../configure --enable-rewrite --enable-so --prefix=/home/ziviani/www
$ make -j 5
$ make install
$ cd /home/ziviani/www
$ ls
bin build cgi-bin conf error htdocs icons include lib logs man manual modules
Check whether the server is working (I set it with high port to run it as a normal user):
$ # still in /home/ziviani/www
$ vi conf/httpd.conf
...
LISTEN 9898
...
ServerName localhost:9898
...
$ bin/apachectl -k start
$ bin/apachectl -k stop
My Module
Now we have the httpd source code that compiles and runs, let’s create our first module.
$ bin/apxs -g -n my_fast_server
Creating [DIR] my_fast_server
Creating [FILE] my_fast_server/Makefile
Creating [FILE] my_fast_server/modules.mk
Creating [FILE] my_fast_server/mod_my_fast_server.c
Creating [FILE] my_fast_server/.deps
$ vim my_fast_server/mod_my_fast_server.c
Easy thing, huh? apxs
just created a template (thanks to -g option) for us. The instructions in the comment section explains how to build and configure it.
1 /*
2 ** mod_my_fast_server.c -- Apache sample my_fast_server module
3 ** [Autogenerated via ``apxs -n my_fast_server -g'']
4 **
5 ** To play with this sample module first compile it into a
6 ** DSO file and install it into Apache's modules directory
7 ** by running:
8 **
9 ** $ apxs -c -i mod_my_fast_server.c
10 **
11 ** Then activate it in Apache's httpd.conf file for instance
12 ** for the URL /my_fast_server in as follows:
13 **
14 ** # httpd.conf
15 ** LoadModule my_fast_server_module modules/mod_my_fast_server.so
16 ** <Location /my_fast_server>
17 ** SetHandler my_fast_server
18 ** </Location>
19 **
20 ** Then after restarting Apache via
21 **
22 ** $ apachectl restart
23 **
24 ** you immediately can request the URL /my_fast_server and watch for the
25 ** output of this module. This can be achieved for instance via:
26 **
27 ** $ lynx -mime_header http://localhost/my_fast_server
28 **
29 ** The output should be similar to the following one:
30 **
31 ** HTTP/1.1 200 OK
32 ** Date: Tue, 31 Mar 1998 14:42:22 GMT
33 ** Server: Apache/1.3.4 (Unix)
34 ** Connection: close
35 ** Content-Type: text/html
36 **
37 ** The sample page from mod_my_fast_server.c
38 */
39 #include "httpd.h"
40 #include "http_config.h"
41 #include "http_protocol.h"
42 #include "ap_config.h"
43
44 /* The sample content handler */
45 static int my_fast_server_handler(request_rec *r)
46 {
47 if (strcmp(r->handler, "my_fast_server")) {
48 return DECLINED;
49 }
50 r->content_type = "text/html";
51
52 if (!r->header_only)
53 ap_rputs("The sample page from mod_my_fast_server.c\n", r);
54 return OK;
55 }
56
57 static void my_fast_server_register_hooks(apr_pool_t *p)
58 {
59 ap_hook_handler(my_fast_server_handler, NULL, NULL, APR_HOOK_MIDDLE);
60 }
61
62 /* Dispatch list for API hooks */
63 module AP_MODULE_DECLARE_DATA my_fast_server_module = {
64 STANDARD20_MODULE_STUFF,
65 NULL, /* create per-dir config structures */
66 NULL, /* merge per-dir config structures */
67 NULL, /* create per-server config structures */
68 NULL, /* merge per-server config structures */
69 NULL, /* table of config file commands */
70 my_fast_server_register_hooks /* register hooks */
71 };
I made some changes in mod_my_fast_server.c
. Nothing big, just make it prints browser’s query string.
1 #include "httpd.h"
2 #include "http_config.h"
3 #include "http_protocol.h"
4 #include "ap_config.h"
5
6 #define MAX_HANDLER 4
7
8 typedef int (*method_handler)(request_rec *r);
9
10 // HTTP Get method handler
11 static int get_handler(request_rec *r);
12
13 // HTTP Post method handler
14 static int post_handler(request_rec *r);
15
16 // HTTP Put method handler
17 static int put_handler(request_rec *r);
18
19 // HTTP Delete method handler
20 static int delete_handler(request_rec *r);
21
22 /* The sample content handler */
23 static int my_fast_server_handler(request_rec *r)
24 {
25 if (strcmp(r->handler, "my_fast_server")) {
26 return DECLINED;
27 }
28 r->content_type = "text/html";
29
30 // as per httpd.h r->method_number gives a numeric representation of http
31 // method: 0 - get, 1 - put, 2 - post, 3 - delete, etc
32 method_handler methods[MAX_HANDLER] = {&get_handler, &put_handler,
33 &post_handler, &delete_handler};
34
35 if (r->method_number >= MAX_HANDLER || r->method_number < 0) {
36 return DECLINED;
37 }
38
39 // call the handler function
40 return methods[r->method_number](r);
41 }
42
43 static int get_handler(request_rec *r)
44 {
45 apr_status_t rv;
46 int i = 0;
47 int n = 0;
48 char* query = r->args; // query string
49
50 // mime type send to the called
51 r->content_type = "text/html";
52
53 // return OK if only header requested or no argument
54 if (r->header_only || r->args == 0) {
55 return OK;
56 }
57
58 ap_rprintf(r, "<h1>[GET] Your query string: %s</h1>", query);
59
60 return OK;
61 }
62
63 // Post http handler
64 static int post_handler(request_rec *r)
65 {
66 return OK;
67 }
68
69 // Put http handler
70 static int put_handler(request_rec *r)
71 {
72 return OK;
73 }
74
75 // Delete http handler
76 static int delete_handler(request_rec *r)
77 {
78 return OK;
79 }
80
81 static void my_fast_server_register_hooks(apr_pool_t *p)
82 {
83 ap_hook_handler(my_fast_server_handler, NULL, NULL, APR_HOOK_MIDDLE);
84 }
85
86 /* Dispatch list for API hooks */
87 module AP_MODULE_DECLARE_DATA my_fast_server_module = {
88 STANDARD20_MODULE_STUFF,
89 NULL, /* create per-dir config structures */
90 NULL, /* merge per-dir config structures */
91 NULL, /* create per-server config structures */
92 NULL, /* merge per-server config structures */
93 NULL, /* table of config file commands */
94 my_fast_server_register_hooks /* register hooks */
95 };
Now, compile it to build a DSO and install that DSO in the modules directory (apxs
does that automatically):
$ bin/apxs -c -i my_fast_server/mod_my_fast_server.c
/home/ziviani/www/build/libtool --silent --mode=compile gcc ...
...
----------------------------------------------------------------------
Libraries have been installed in:
/home/ziviani/www/modules
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the '-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the 'LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the 'LD_RUN_PATH' environment variable
during linking
- use the '-Wl,-rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to '/etc/ld.so.conf'
See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
chmod 755 /home/ziviani/www/modules/mod_my_fast_server.so
And that’s all! You only need to configure httpd.conf
, start the server and use it.
$ vim conf/httpd.conf
...
LoadModule my_fast_server_module modules/mod_my_fast_server.so
<Location /my_fast_server>
SetHandler my_fast_server
</Location>
...
$ bin/apachectl -k start
Open your browse and navigate it to http://localhost:9898/my_fast_server?test&query=bla
to see the query string printed.
Extra: static (no apxs) build
Suppose you don’t want to use apxs
and want to build your module together with httpd itself.
I’ll reuse the same source file and add my module in the Apache httpd build system. The steps are following:
- copy the my_fast_server folder into httpd main source folder;
- add my_fast_server in httpd build system;
- call buildconf again to re-generate autoconf;
- build!
Example:
$ cp -a my_fast_server ~/httpd/modules/
$ cd ~/httpd
$ vim my_fast_server/config.m4
APACHE_MODPATH_INIT(my_fast_server)
APACHE_MODULE(my_fast_server, My FAST server!, , , no)
APACHE_MODPATH_FINISH
$ vim my_fast_server/Makefile.in
include $(top_srcdir)/build/special.mk
$ ./buildconf
$ cd mybuild
$ ../configure --help
...
--disable-version determining httpd version in config files
--enable-remoteip translate header contents to an apparent client
remote_ip
--enable-my-fast-server My FAST!! server <===== Yay!!!
--enable-proxy Apache proxy module
--enable-proxy-connect Apache proxy CONNECT module. Requires
...
$ CFLAGS="-O0 -ggdb" ../configure --enable-rewrite --enable-so --enable-my-fast-server --prefix=/home/ziviani/www
$ make
$ make install
If the build finished without any errors, you can test your new module. Go to the www
folder, edit conf/httpd.conf
and start the server.
$ vim conf/httpd.conf
...
LoadModule my_fast_server_module modules/mod_my_fast_server.so
<Location /my_fast_server>
SetHandler my_fast_server
</Location>
...
$ bin/apachectl -k start
Navigate it to http://localhost:9898/my_fast_server?test&query=bla
to see the query string printed.