Apache C API

Copyright 2002 Thomas Eibner, All Rights Reserved

Getting started using the Apache C API (For versions 1.3.x)

In this short introduction to Apache's C API we will create a few basic modules you will be able to extend to fit your need or just give you a taste of what you can do with it.

Basic knowledge of the C programming language is needed to be able to use this introduction, but examples are kept very down to earth and might be understandable if you know another programming language.

The few common tasks we create a module for are:

Before we start to get down to the hot stuff and write our first modules we will take a look at what actually makes up a module in Apache. We will take a look at what is required and which functions the different headerfiles allows you to use.

What makes up a module for Apache?

To be able to write a module for Apache you need to include some headerfiles from the Apache source tree that defines the C API to Apache. For our first simple examples we will only need two headerfiles:

#include "httpd.h"
#include "http_config.h"

httpd.h will include defines, structures, and common functions for all operations related to the Apache server. Including, but not limited to, http status codes, table functions, and much more.

http_config.h includes definitions for the module slot of hooks into Apache. Functions for configuration structures are also exported from this module. We will be taking a look at them in a little while.

From http_config.h we get the structure module which we need to use to get our module hooked into the different phases of a request. In the same file a helping definition for the first 6 of the slots is defined, namely STANDARD_MODULE_STUFF, which fills out some important parts of the module struct. Among others it fills in the API major version, API minor version, and the module magic cookie.

To see how the structures look like, you will need to look into apache-1.3.x/src/include/http_config.h. The following code snippet would not hook any of the functions defined within the file into any of the phases, so the module would basically not do anything when loaded.

module MODULE_VAR_EXPORT name_module = {
    STANDARD_MODULE_STUFF,
    NULL,                              /* initializer */
    NULL,                              /* dir config creator */
    NULL,                              /* dir config merger */
    NULL,                              /* server config creator */
    NULL,                              /* server config merger */
    NULL,                              /* command table */
    NULL,                              /* handlers */
    NULL,                              /* filename translation */
    NULL,                              /* check_user_id */
    NULL,                              /* check auth */
    NULL,                              /* check access */
    NULL,                              /* type_checker */
    NULL,                              /* fixups */
    NULL,                              /* logger */
    NULL,                              /* header parser */
    NULL,                              /* child_init */
    NULL,                              /* child_exit */
    NULL                               /* post read-request */
};

What all the different comments (phases) means will be explained in the next section, Request phases

Request phases

The different request phases are described in an order which seems most appropriate, which means it's not in the order that the module struct expects them. For every phase the prototype for the function and the return values are showed.

The "initializer" phase

If you hook a function into this phase it would be run when the server is initialized. Functions that need to be started in this phase is usualy one that sets a part of the outgoing Server header.

static void modulename_init(server_rec *s, pool *p);

Return value: None.

The "child_init" phase

If you're looking for a place to stick something that will be persistant across the life of the child, like putting initial values into variables, this is the place to do so. You cannot get access to any configuration options in this phase, so initializing persistant connections to databases is not something you would do here unless you want to hardcode configuration of the connections.

static void modulename_child_init(server_rec *s, pool *p);

Return value: None.

The "child_exit" phase

This function is called when one of the Apache childs exit, so it is perfect to shutdown persistant connections from.

static void modulename_child_exit(server_rec *s, pool *p);

Return value: None.

The "server config creator" phase

This phase should initialize a server config structure if any is used.

static void *modulename_create_server_config(pool *p, server_rec *s);

Return value: pointer to the created module-specific structure.

The "server config merger" phase

This phase creates a merged server config structure of the two supplied server config structures. The function may not modify any of it's arguments.

static void *modulename_merge_server_config(pool *p, void *server1_conf, void *server2_conf);

Return value: pointer to the created module-specific structure containing the merged values.

The "directory config creator" phase

This phase creates a directory config structure if any is used. It is used to keep configuration that is specific to a directory.

static void *modulename_create_dir_config(pool *p, char *dirspec);

Return value: pointer to the created directory-specific structure.

The "directory config merger" phase

This phase creates a merged directory config structure of the two supplied directory config structures. The function may not modify any of it's arguments.

static void *modulename_merge_dir_config(pool *p, void *parent_conf, void *newloc_conf);

Return value: pointer to the created directory-specific structure containing the merged values.

The "post read-request" phase

This phase allows you to put a hook in right after where the request has been read from the client but before any other phases have been initiated.

static int modulename_post_read_request(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. If OK is returned no other handlers are called for this phase.

The "header parser" phase

If you need to work with any of the headers and influence any of the later phases this is the place to register your calls.

static int modulename_header_parser(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. If OK is returned the remaining modules that have handlers registered for this phase will still be called.

The "filename translation" phase

This phase is where the translation from an URI to a filename happens.

static int modulename_translate_filename(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. Returning OK will stop other handlers from handling this phase.

The "check_user_id" phase

Check authentication information sent, username, password.

static int modulename_check_user_id(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble error, typically HTTP_UNAUTHORIZED. If OK is returned no other handlers for this phase are checked.

The "check auth" phase

This phase is used to check wheter the resource the client is trying to access is protected by some kind of authorization.

static int modulename_check_auth(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. If OK is returned no other handlers for this phase are checked. If all registered handlers for this phase returns DECLINED the server aborts the request with a server error.

The "check access" phase

This phase is used to place module-specific restrictions on access to a resource..

static int modulename_check_access(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. All handlers will be called regardless if the others return either OK or DECLINED. Any other return value causes the request to be aborted.

The "type_checker" phase

This phase checks the mime type of the request and sets the content_type acordingly.

static int modulename_check_type(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. If OK is returned no other handlers for this phase are checked.

The "fixup" phase

Last minute fixing of header fields are preformed in this phase, which is invoked just before any content handlers are called.

static int modulename_fixup(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. Even if OK is returned the other registered handlers for this phase are called.

The "logger" phase

If you are interested in logging any of the actions performed in the request you want to hook your handlers into this phase. Return values are OK, DECLINED, HTTP_mumble and if you return OK all other handlers that are registered for this phase will still be called.

static int modulename_logger(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. All handlers will be called for this phase regardless of wheter we return OK here.

The "handler" phase

This is the phase where the module serves any content or does whatever it needs to do. This type of handler doesn't go into the handler slot in the module structure. A wrapper structure called handler_rec is used for storing each content handler. All the content handlers are put into an array of handler_rec structures and then put into the module structure where all the other handlers go.

static int modulename_handler(request_rec *r);

Return value: OK, DECLINED, or HTTP_mumble. OK means everything went okay. DECLINED means we don't want to serve this request. Everything else is reported as an error.

The handler_rec structure

The handler_rec structure is defined in http_config.h. It looks like this:

typedef struct {
    const char *content_type;	/* MUST be all lower case */
    int (*handler) (request_rec *);
} handler_rec;

content_type is what you use to hook the content handler into a location from inside httpd.conf. handler is the function you want called from inside your module. So we end up with this for our module:

static const handler_rec modulename_handlers[] = {
    {"modulename-handler", modulename_handler},
    {NULL}
};

It will now be possible to enabled this with the use of SetHandler modulename-handler in httpd.conf



Creating configuration directives for Apache



The command_rec structure

The command_rec structure is likewise defined in http_config.h. It looks like this:

typedef struct command_struct {
    const char *name;		/* Name of this command */
    const char *(*func) ();	/* Function invoked */
    void *cmd_data;		/* Extra data, for functions which
				 * implement multiple commands...
				 */
    int req_override;		/* What overrides need to be allowed to
				 * enable this command.
				 */
    enum cmd_how args_how;	/* What the command expects as arguments */

    const char *errmsg;		/* 'usage' message, in case of syntax errors */
} command_rec;

It has 6 slots that needs to be filled out with information about the command you want to create.



command table

So far the different phases have been pretty straight forward to manipulate, but the two remaining phases are a bit trickier.

Command table: TAKE123 etc..



The completed structure

yada

module MODULE_VAR_EXPORT name_module = {
    STANDARD_MODULE_STUFF,
    modulename_init,                   /* initializer */
    modulename_create_dir_config,      /* dir config creator */
    modulename_merge_dir_config,       /* dir config merger */
    modulename_create_server_config,   /* server config creator */
    modulename_merge_server_config,    /* server config merger */
    modulename_handlers,               /* command table */
    modulename_,                              /* handlers */
    modulename_translate_filename,     /* filename translation */
    modulename_check_user_id,          /* check_user_id */
    modulename_check_auth,             /* check auth */
    modulename_check_access,           /* check access */
    modulename_check_type,             /* type_checker */
    modulename_fixup,                  /* fixups */
    modulename_logger,                 /* logger */
    modulename_header_parser,          /* header parser */
    modulename_child_init,             /* child_init */
    modulename_child_exit,             /* child_exit */
    modulename_post_read_request       /* post read-request */
};


Other things

request_rec object



Compiling your modules

Before you can use the module created it needs to be compiled. There are two ways of doing this. One is compiling it directly into Apache, the other is making a dynamic loadable module. We'll go over both methods here.

Statically / compiling it into Apache

Since it's pretty well documented how to compile Apache we wont go into details on this here, but we will just show you what you need to do to get your module recognized and included by Apache:

$ cp /path/to/mod_my.c src/modules/extra/
$ ./configure [your normal options go here] --activate-module=src/modules/extra/mod_my.c \
     --enable-module=my
$ make

The task of compiling the module into the server is pretty trivial, so building scripts to do this is not a bad idea.

Dynamically / apxs

It's easy compiling your modules with apxs, which by the way means APache eXtenSion tool. You might want to read the man page of apxs to learn all of it's features. To use this feature you will need to have your Apache with the module mod_so compiled in. We will just cover the basics of compiling a simple module:

$ apxs -Wall -c -o mod_my.so mod_my.c

That is all there is to it. Now you just need to load the module in the Apache config like this:

LoadModule my_module path/to/mod_my.so

Again, the task of compiling should be automated to be less error-prone. I created a Makefile for that purpose:

# Generic Makefile for GNU make (gmake)
#APXS=$(shell which apxs) # or hardcode to path of apxs
APXS=/home/thomas/build/apache-dev/bin/apxs
SOURCES:=$(wildcard mod_*.c)
OBJS=$(foreach SOURCE,$(SOURCES),$(subst .c,.so,$(SOURCE)))
INST_OBJS=$(foreach SOURCE,$(SOURCES),$(subst .c,,$(SOURCE)))
CFLAGS:=`$(APXS) -q CFLAGS`

modules: $(OBJS)
	@echo make modules finished, type make install to install modules

install: $(INST_OBJS)
	@echo installed modules

clean:
	rm -rf *.so *.o *~

$(OBJS):
	$(APXS) -c -S CFLAGS="$(CFLAGS) -g" -Wall -Wc -o $@ $(subst .so,.c,$@)

$(INST_OBJS): $(OBJS)
	$(APXS) -i -n $@ $@.so

This Makefile will try to guess the path to apxs. If apxs is in your path you will be safe, otherwise you will need to edit the APXS line of the Makefile to fith your install. The Makefile will automatically find the module(s) in the directory if you stick to the simple convention of mod_*.c, which it will compile into mod_*.so. It was designed to work with GNU make, so you will have to edit it to make it work with a regular make or install GNU make.



Debugging

Writing modules for Apache has been somewhat of a mixed affair for me. It's been fun and I've learned a lot, but a lot of times I've spent hours wondering what was wrong with my module. Apache would just segfault and not give a clue to what was wrong. This is where debuggers come to the rescue.

Building an Apache for debugging

First of all we need to recompile our Apache httpd binary with the added debugging flags. This is done by defining CFLAGS=-g

$ ls
apache_1.3.20.tar.gz
$ tar -zxf apache_1.3.20.tar.gz
$ ls -F
apache_1.3.20/  apache_1.3.20.tar.gz
$ cd apache_1.3.20/
$ CFLAGS="-g" ./configure --prefix=/home/thomas/build/apache-dev \
    --enable-module=so
[output of configure]
$ make
$ make install
Now you need to edit the configuration file httpd.conf to fit your needs or just use this one to test out your modules:
ServerType standalone
ServerRoot "/home/thomas/build/apache-dev"
ServerName localhost
PidFile /home/thomas/build/apache-dev/logs/httpd.pid
ScoreBoardFile /home/thomas/build/apache-dev/logs/httpd.scoreboard
Timeout 300
Port 8080
User thomas
Group thomas
ServerAdmin thomas@localhost
DocumentRoot "/home/thomas/build/apache-dev/htdocs"
<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory "/home/thomas/build/apache-dev/htdocs">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>
<IfModule mod_dir.c>
    DirectoryIndex index.html
</IfModule>
AccessFileName .htaccess
<Files ~ "^\.ht">
    Order allow,deny
    Deny from all
</Files>
ErrorLog /home/thomas/build/apache-dev/logs/error_log
LogLevel warn
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog /home/thomas/build/apache-dev/logs/access_log combined

<IfDefine HEADER>
    LoadModule my_header_module libexec/mod_my_header.so
</IfDefine>
<IfDefine SERVER>
    LoadModule my_server_module libexec/mod_my_server.so
</IfDefine>
<IfDefine ADVANCED_HEADER>
    LoadModule my_advanced_header_module libexec/mod_my_advanced_header.so
    MyAdvancedHeader On
    MyAdvancedHeader_message "this is a test-header"
</IfDefine>

If you use the configuration file supplied here you will be able to start your server in single-process mode like this:

$ /home/thomas/build/apache-dev/bin/httpd -X -DHEADER
# This will load mod_my_header.so
$ /home/thomas/build/apache-dev/bin/httpd -X -DSERVER
# This will load mod_my_server.so
$ /home/thomas/build/apache-dev/bin/httpd -X -DADVANCED_HEADER
# This will load mod_my_advanced_header.so

As you can see it is pretty easy and it is just as easy to load it up in your debugger, which the next section is all about.

Be aware of problems debugging code with loadable modules

gdb

The most widely used debugger under *NIX environment is gdb, also called The GNU Debugger.

I created a .gdbinit file to make the debugging easier. Please note that there might be a bug in the dump_tables define.

define httpd
    run -X -f /home/thomas/build/apache-dev/conf/httpd.conf
end

define dump_table
    set $t = (table_entry *)((array_header *)$arg0)->elts
    set $n = ((array_header *)$arg0)->nelts
    set $i = 0
    while $i < $n
	printf "[%u] '%s'='%s'\n", $i, $t[$i].key, $t[$i].val
	set $i = $i + 1
    end
end
document dump_table
    Print the key/value pairs in a table.
end

define dump_string_array
    set $a = (char **)((array_header *)$arg0)->elts
    set $n = (int)((array_header *)$arg0)->nelts
    set $i = 0
    while $i < $n
	printf "[%u] '%s'\n", $i, $a[$i]
	set $i = $i + 1
    end
end
document dump_string_array
    Print all of the elements in an array of strings.
end

define dump_request
    set $r = $arg0
    printf "[request_rec dump]\n"
    if $r->the_request != 0x0
        printf "r->the_request      = '%s'\n", $r->the_request
    end
    printf "r->proxyreq         = %u\n", $r->proxyreq
    printf "r->header_only      = %u\n", $r->header_only
    if $r->protocol != 0x0
        printf "r->protocol         = '%s'\n", $r->protocol
    end
    printf "r->proto_num        = %u\n", $r->proto_num
    if $r->hostname != 0x0
        printf "r->hostname         = '%s'\n", $r->hostname
    end
    printf "r->request_time     = %u\n", $r->request_time
    if $r->status_line != 0x0
        printf "r->startus_line     = '%s'\n", $r->status_line
    end
    printf "r->status           = %u\n", $r->status
    if $r->method != 0x0
        printf "r->method           = '%s'\n", $r->method
    end
    printf "r->method_number    = %u\n", $r->method_number
    printf "r->allowed          = %u\n", $r->allowed
    printf "r->sent_bodyct      = %u\n", $r->sent_bodyct
    printf "r->bytes_sent       = %u\n", $r->bytes_sent
    printf "r->mtime            = %u\n", $r->mtime
    printf "r->chunked          = %u\n", $r->chunked
    printf "r->byterange        = %u\n", $r->byterange
    if $r->boundary != 0x0
        printf "r->boundary         = '%s'\n", $r->boundary
    end
    if $r->range != 0x0
        printf "r->range            = '%s'\n", $r->range
    end
    printf "r->clength          = %u\n", $r->clength
    printf "r->remaining        = %u\n", $r->remaining
    printf "r->read_length      = %u\n", $r->read_length
    printf "r->read_body        = %u\n", $r->read_body
    printf "r->read_chunked     = %u\n", $r->read_chunked
    printf "r->expecting_100    = %u\n", $r->expecting_100
    if $r->content_type != 0x0
        printf "r->content_type     = '%s'\n", $r->content_type
    end
    if $r->handler != 0x0
        printf "r->handler          = '%s'\n", $r->handler
    end
    if $r->content_encoding != 0x0
        printf "r->content_encoding = '%s'\n", $r->content_encoding
    end
    if $r->content_language != 0x0
        printf "r->content_language = '%s'\n", $r->content_language
    end
    if $r->vlist_validator != 0x0
        printf "r->vlist_validator  = '%s'\n", $r->vlist_validator
    end
    printf "r->no_cache         = %u\n", $r->no_cache
    printf "r->no_local_copy    = %u\n", $r->no_local_copy
    if $r->unparsed_uri != 0x0
        printf "r->unparsed_uri     = '%s'\n", $r->unparsed_uri
    end
    if $r->uri != 0x0
        printf "r->uri              = '%s'\n", $r->uri
    end
    if $r->filename != 0x0
        printf "r->filename         = '%s'\n", $r->filename
    end
    if $r->path_info != 0x0
        printf "r->path_info        = '%s'\n", $r->path_info
    end
    if $r->args != 0x0
        printf "r->args             = '%s'\n", $r->args
    end
end

define dump_tables
    printf "[r->headers_in]\n"
    dump_table r->headers_in
    printf "[r->headers_out]\n"
    dump_table r->headers_out
    printf "[r->err_headers_out]\n"
    dump_table r->err_headers_out
    printf "[r->subprocess_env]\n"
    dump_table r->subprocess_env
    printf "[r->notes]\n"
    dump_table r->notes
end

I really didn't feel that comfortable using gdb so I tried on of the graphical frontends to gdb; DDD.

DDD

DDD soon became a tool I really liked to use. I like it because it's much easier to see what is going on inside the code. With your cursor you can inspect values of variables and create breakpoints. DDD is downloadable from http://www.gnu.org/software/ddd/ddd.html. Starting DDD with your httpd binary is straightforward:

ddd ./httpd


Putting it all together

After digesting all this information we're going to put some of the theory to work. The first example will be adding a custom header to the outgoing response.

CREATE DESCRIPTION OF EACH SINGLE FUNCTION INSIDE EACH MODULE

Adding a custom header

yada yada yada about the module

#include "httpd.h"
#include "http_config.h"

module MODULE_VAR_EXPORT my_header_module;

static int my_header_fixup(request_rec *r) {
    ap_table_set(r->headers_out, "X-My-Header", "this is my header");
    return OK;
}

module MODULE_VAR_EXPORT my_header_module = {
    STANDARD_MODULE_STUFF,
    NULL,                              /* initializer */
    NULL,                              /* dir config creator */
    NULL,                              /* dir config merger */
    NULL,                              /* server config */
    NULL,                              /* merge server config */
    NULL,                              /* command table */
    NULL,                              /* handlers */
    NULL,                              /* filename translation */
    NULL,                              /* check_user_id */
    NULL,                              /* check auth */
    NULL,                              /* check access */
    NULL,                              /* type_checker */
    my_header_fixup,                   /* fixups */
    NULL,                              /* logger */
    NULL,                              /* header parser */
    NULL,                              /* child_init */
    NULL,                              /* child_exit */
    NULL                               /* post read-request */
};

output from module:

$ lwp-request -m HEAD http://localhost/
200 OK
Connection: close
Date: Mon, 30 Jul 2001 20:00:43 GMT
Accept-Ranges: bytes
Server: Apache/1.3.20 (Unix) Debian/GNU
Content-Length: 4100
Content-Type: text/html; charset=iso-8859-1
ETag: "10a979-1004-3b26716d"
Last-Modified: Tue, 12 Jun 2001 19:45:49 GMT
Client-Date: Mon, 30 Jul 2001 20:00:43 GMT
Client-Peer: 127.0.0.1:80
X-My-Header: this is my header

As you see, we have created a module which outputs a header with every response. Now wasn't that simple?

Adding your own module's name to the Server header

A lot of people have asked this question; How do I change the outgoing server header? There are two ways to do this. One is directly changing the initial value in the sources, which would be not following the guidelines set out by the Apache group. Since we only want to add our own module to the outgoing Server header we are going to use the function ap_add_version_component exported by httpd.h. We use this function in the server_init phase to achieve our goal.

#include "httpd.h"
#include "http_config.h"

#define MY_SERVER_VERSION "1.0"

module MODULE_VAR_EXPORT my_server_module;

static void my_server_init(server_rec *c, pool *p) {
    ap_add_version_component("mod_my_server/" MY_SERVER_VERSION);
}

module MODULE_VAR_EXPORT my_server_module = {
    STANDARD_MODULE_STUFF,
    my_server_init,                    /* initializer */
    NULL,                              /* dir config creator */
    NULL,                              /* dir config merger */
    NULL,                              /* server config */
    NULL,                              /* merge server config */
    NULL,                              /* command table */
    NULL,                              /* handlers */
    NULL,                              /* filename translation */
    NULL,                              /* check_user_id */
    NULL,                              /* check auth */
    NULL,                              /* check access */
    NULL,                              /* type_checker */
    NULL,                              /* fixups */
    NULL,                              /* logger */
    NULL,                              /* header parser */
    NULL,                              /* child_init */
    NULL,                              /* child_exit */
    NULL                               /* post read-request */
};

output from module:

$ lwp-request -m HEAD http://localhost/ 
200 OK
Connection: close
Date: Mon, 30 Jul 2001 20:00:43 GMT
Accept-Ranges: bytes
Server: Apache/1.3.20 (Unix) Debian/GNU mod_my_server/1.0
Content-Length: 4100
Content-Type: text/html; charset=iso-8859-1
ETag: "10a979-1004-3b26716d"
Last-Modified: Tue, 12 Jun 2001 19:45:49 GMT
Client-Date: Mon, 30 Jul 2001 20:00:43 GMT
Client-Peer: 127.0.0.1:80

Again, it is very easy to create these "effects"

Adding configuration directives to your module

Now we want to create a more advanced module. We are going to be doing the same as in our first module; display a header with every response. To make it more advanced we want to be able to turn it On and Off from inside httpd.conf. Additionally we want to be able to customize the header message from inside the configuration file.

#include "httpd.h"
#include "http_config.h"

typedef struct {
    char *header_message;
    int header_on;
} my_advanced_header_server_cfg;

module MODULE_VAR_EXPORT my_advanced_header_module;

static void *my_advanced_header_server_config(pool *p, server_rec *c) {
    my_advanced_header_server_cfg *cfg = ap_pcalloc(p, sizeof(my_advanced_header_server_cfg));
    if (!cfg)
        return NULL;

    cfg->header_message = "fill me out";
    cfg->header_on = 0;

    return (void *)cfg;
}

static const char *my_advanced_header_set_header(cmd_parms *cmd, void *dummy, char *header) {
    server_rec *s = cmd->server;
    my_advanced_header_server_cfg *cfg = (my_advanced_header_server_cfg *)ap_get_module_config(s->module_config, &my_advanced_header_module);
    cfg->header_message = header;
    return NULL;
}

static const char *my_advanced_header_set_header_on(cmd_parms *cmd, void *dummy, int flag) {
    server_rec *s = cmd->server;
    my_advanced_header_server_cfg *cfg = (my_advanced_header_server_cfg *)ap_get_module_config(s->module_config, &my_advanced_header_module);
    cfg->header_on = flag;
    return NULL;
}

static command_rec my_advanced_header_cmds[] = {
    { "MyAdvancedHeader", my_advanced_header_set_header_on, NULL,
      RSRC_CONF, FLAG, "turn on my_advanced_header's output" },
    { "MyAdvancedHeader_message", my_advanced_header_set_header, NULL,
      RSRC_CONF, TAKE1, "the message we want to output in our header" },
    { NULL }
};

static int my_header_fixup(request_rec *r) {
    my_advanced_header_server_cfg *cfg = (my_advanced_header_server_cfg *)ap_get_module_config(r->server->module_config, &my_advanced_header_module);
    if (!cfg->header_on)
        return DECLINED;
    ap_table_set(r->headers_out, "X-My-Header", cfg->header_message);
    return OK;
}

module MODULE_VAR_EXPORT my_advanced_header_module = {
    STANDARD_MODULE_STUFF,
    NULL,                              /* initializer */
    NULL,                              /* dir config creator */
    NULL,                              /* dir config merger */
    my_advanced_header_server_config,  /* server config */
    NULL,                              /* merge server config */
    my_advanced_header_cmds,           /* command table */
    NULL,                              /* handlers */
    NULL,                              /* filename translation */
    NULL,                              /* check_user_id */
    NULL,                              /* check auth */
    NULL,                              /* check access */
    NULL,                              /* type_checker */
    my_header_fixup,                   /* fixups */
    NULL,                              /* logger */
    NULL,                              /* header parser */
    NULL,                              /* child_init */
    NULL,                              /* child_exit */
    NULL                               /* post read-request */
};

output from module:

$ yada

yada



More information

Explore the source code of Apache and accompanying modules, especially mod_example.c

Writing Apache modules with Perl and C (http://www.modperl.com/) Published by O'Reilly. ISBN: 156592567X

How to write a Makefile

apache-modules mailinglist hosted by Covalent.


Warning: include(../../comments.php) [function.include]: failed to open stream: No such file or directory in /var/www/thomas.eibner.dk/apache/api.html on line 416

Warning: include() [function.include]: Failed opening '../../comments.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/thomas.eibner.dk/apache/api.html on line 416
What is STANDARD_MODULE_STUFF? Why is there all the NULL's there?