From e3c334c6c58afeb5b1a045ea6181a2a441c7b429 Mon Sep 17 00:00:00 2001
From: Daniel Gruno Apache HTTP Server Version 2.5 Available Languages: en This document explains how you can develop modules for the Apache HTTP Server 2.4
+This document will discuss how you can easily create modules for the Apache HTTP Server 2.4 ("Apache"),
+by exploring an example module called
+In the second part of this document, which deals with configuration directive and context awareness, we will be looking at
+a module that simply write out its own configuration to the client.
+
+First and foremost, you are expected to have a basic knowledge of how the C programming language works. In most cases,
+we will try to be as pedagogical as possible and link to documents describing the functions used in the examples,
+but there are also many cases where it is necessary to either just assume that "it works" or do some digging youself
+into what the hows and whys of various function calls.
+
+Lastly, you will need to have a basic understanding of how modules are loaded and configured in Apache, as well as
+how to get the headers for Apache if you do not have them already, as these are needed for compiling new modules.
+
+To compile the source code we are building in this document, we will be using APXS.
+Assuming your source file is called mod_example.c, compiling, installing and activating the module is as simple as:
+ Every module starts with the same declaration, or name tag if you will, that defines a module as a separate entity within Apache:
+
+
+
+
+Within this name tag of ours is also a bunch of references to how we would like to handle things:
+Which directives do we respond to in a configuration file or .htaccess, how do we operate
+within specific contexts, and what handlers are we interested in registering with the Apache
+service. We'll return to all these elements later in this document.
+
+When handling requests in Apache, the first thing you will need to do is create a hook into
+the request handling process. A hook is essentially a message telling Apache that you are
+willing to either serve or at least take a glance at certain requests given by clients. All
+handlers, whether it's mod_rewrite, mod_authn_*, mod_proxy and so on, are hooked into specific
+parts of the request process. As you are probably aware, modules serve different purposes; Some
+are authentication/authorization handlers, others are file or script handlers while some third
+modules rewrite URIs or proxies content. Furthermore, in the end, it is up to the user of Apache
+how and when each module will come into place. Thus, Apache itself does not presume to know
+which module is responsible for handling a specific request, and will ask each module whether
+they have an interest in a given request or not. It is then up to each module to either gently
+decline serving a request, accept serving it or flat out deny the request from being served,
+as authentication/authorization modules do:
+To begin with, we only want to create a simple handler, that replies to the client browser
+when a specific URL is requested, so we won't bother setting up configuration handlers and
+directives just yet. Our initial module definition will look like this:
+The reference in our example declaration,
+Hooking into the request handling phase is but one of many hooks that you can create. Some other ways of hooking are:
+
+A handler is essentially a function that receives a callback when a request to Apache is made.
+It is passed a record of the current request (how it was made, which headers and requests were
+passed along, who's giving the request and so on), and is put in charge of either telling
+Apache that it's not interested in the request or handle the request with the tools provided.
+ The most essential part of any request is the request record. In a call to a handler function, this
+is represented by the Some key elements of the
+Let's try out some of these variables in another example handler:
+Apache relies on return values from handlers to signify whether a request was handled or not,
+and if so, whether the request went well or not. If a module is not interested in handling
+a specific request, it should always return the value
+Managing your resources in Apache is quite easy, thanks to the memory pool system. In essence,
+each server, connection and request have their own memory pool that gets cleaned up when its
+scope ends, e.g. when a request is done or when a server process shuts down. All your module
+needs to do is latch onto this memory pool, and you won't have to worry about having to clean
+up after yourself - pretty neat, huh?
+In our module, we will primarilly be allocating memory for each request, so it's appropriate to
+use the
+In our example module, we would like to add a feature, that checks which type of digest, MD5 or SHA1
+the client would like to see. This could be solved by adding a query string to the request. A query
+string is typically comprised of several keys and values put together in a string, for instance
+
+Since the introduction of Apache 2.4, parsing request data from GET and POST requests have never
+been easier. All we require to parse both GET and POST data is four simple lines:
+
+
+
+
+In this next segment of this document, we will turn our eyes away from the digest module and create a new
+example module, whose only function is to write out its own configuration. The purpose of this is to
+examine how Apache works with configuration, and what happens when you start writing advanced configurations
+for your modules.
+
+As you can see, each directive needs at least 5 parameters set:
+
+Now that we've told Apache to expect some directives for our module, it's time to make a few functions for handling these. What
+Apache reads in the configuration file(s) is text, and so naturally, what it passes along to our directive handler is one or
+more strings, that we ourselves need to recognize and act upon. You'll notice, that since we set our
+Now that we have our directives set up, and handlers configured for them, we can assemble our module
+into one big file:
+
+
+
+
+In our httpd.conf file, we can now change the hard-coded configuration by adding a few lines:
+
+In Apache, different URLs, virtual hosts, directories etc can have very different meanings
+to the user of Apache, and thus different contexts within which modules must operate. For example,
+let's assume you have this configuration set up for mod_rewrite:
+
+So how does a module get the specific configuration for the server, directory or location in question? It does so by making one simple call:
+
+
+
+ In this chapter, we will be working with a slightly modified version of our previous
+context structure. We will set a Our handler for requests will also be modified, yet still very simple:
+
+
+
+
+Before we can start making our module context aware, we must first define, which contexts we will accept.
+As we saw in the previous chapter, defining a directive required five elements be set:
+
+
+
+ A much smarter way to manage your configurations is by letting Apache help you create them.
+To do so, we must first start off by chancing our name tag to let Apache know, that
+it should assist us in creating and managing our configurations. Since we have chosen the per-directory
+(or per-location) context for our module configurations, we'll add a per-directory creator and merger
+function reference in our tag:
+
+
+
+Now that we have told Apache to help us create and manage configurations, our first step is to
+make a function for creating new, blank configurations. We do so by creating the function we just
+referenced in our name tag as the Per-directory configuration handler:
+
+
+Our next step in creating a context aware configuration is merging configurations. This part of the process
+particularly apply to scenarios where you have a parent configuration and a child, such as the following:
+
+Now, let's try putting it all together to create a new module that it context aware. First off, we'll
+create a configuration that lets us test how the module works:
+
+We have now looked at how to create simple modules for Apache and configuring them. What you do next is entirely up
+to you, but it is my hope that something valuable has come out of reading this documentation. If you have questions
+on how to further develop modules, you are welcome to join our mailing lists
+or check out the rest of our documentation for further tips.
+ Available Languages: en This document explains how you can develop modules for the Apache HTTP Server 2.4
+This document will discuss how you can easily create modules for the Apache HTTP Server 2.4 ("Apache"),
+by exploring an example module called
+In the second part of this document, which deals with configuration directive and context awareness, we will be looking at
+a module that simply write out its own configuration to the client.
+
+First and foremost, you are expected to have a basic knowledge of how the C programming language works. In most cases,
+we will try to be as pedagogical as possible and link to documents describing the functions used in the examples,
+but there are also many cases where it is necessary to either just assume that "it works" or do some digging youself
+into what the hows and whys of various function calls.
+
+Lastly, you will need to have a basic understanding of how modules are loaded and configured in Apache, as well as
+how to get the headers for Apache if you do not have them already, as these are needed for compiling new modules.
+
+To compile the source code we are building in this document, we will be using APXS.
+Assuming your source file is called mod_example.c, compiling, installing and activating the module is as simple as:
+ Every module starts with the same declaration, or name tag if you will, that defines a module as a separate entity within Apache:
+
+
+
+
Developing modules for the Apache HTTP Server 2.4
+
Introduction
Defining a module
Getting started: Hooking into Apache
Building a handler
Adding configuration options
Context aware configurations
Summing up
Some useful snippets of codeSee also
Introduction
+What we will be discussing in this document
+mod_example. In the first part of this document, the purpose of this
+module will be to calculate and print out various digest values for existing files on your web server, whenever we
+access the URL http://hostname/filename.sum. For instance, if we want to know the MD5 digest value of the file
+located at http://www.example.com/index.html, we would visit http://www.example.com/index.html.sum.
+Prerequisites
+Compiling your module
+
+apxs -i -a -c mod_example.c
+
Defining a module
+
+
+module AP_MODULE_DECLARE_DATA example_module
+
+
+
+This bit of code lets Apache know that we have now registered a new module in the system,
+and that its name is =
+{
+ STANDARD20_MODULE_STUFF,
+ create_dir_conf, /* Per-directory configuration handler */
+ merge_dir_conf, /* Merge handler for per-directory configurations */
+ create_svr_conf, /* Per-server configuration handler */
+ merge_svr_conf, /* Merge handler for per-server configurations */
+ directives, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+example_module. The name of the module is used primarilly
+for two things:
+
+
+For now, we're only concerned with the first purpose of the module name,
+which comes into play when we need to load the module:
+LoadModule example_module modules/mod_example.so
mod_example.so and look for a module
+called example_module.
+
+Getting started: Hooking into Apache
+An introduction to hooks
+
+
+To make it a bit easier for handlers such as our mod_example to know whether the client is
+requesting content we should handle or not, Apache has directives for hinting to modules whether
+their assistance is needed or not. Two of these are AddHandler and SetHandler.
+Let's take a look at an example using AddHandler.
+In our example case, we want every request ending with .sum to be served by mod_example,
+so we'll add a configuration directive that tells Apache to do just that:
+
+AddHandler example-handler .sum
+
AddHandler and
+reply to Apache based on the value of this tag.
+
+
+Hooking into httpd
+
+
+
+
+
+module AP_MODULE_DECLARE_DATA example_module =
+{
+ STANDARD20_MODULE_STUFF,
+
+
+
+
+This lets Apache know that we are not interesting in anything fancy, we just want to hook onto
+the requests and possibly handle some of them.
+
+NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ register_hooks
+};
+/* Our hook registering function */register_hooks is the name of a function
+we will create to manage how we hook onto the request process. In this example module, the function
+has just one purpose; To create a simple hook that gets called after all the rewrites, access
+control etc has been handled. Thus, we will let Apache know, that we want to hook into its process
+as one of the last modules:
+
+
+
+
+
+
+
+
+The static void register_hooks(apr_pool_t *pool)
+{
+ /* Create a hook in the request handler, so we get called when a request arrives */
+ ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
+}
+example_handler reference is the function that will handle the request. We will
+discuss how to create a handler in the next chapter.
+
+
+Other useful hooks
+
+
+
+
+ap_hook_child_init: Place a hook that executes when a child process is spawned (commonly used for initializing modules after Apache has forked)ap_hook_pre_config: Place a hook that executes before any configuration data has been read (very early hook)ap_hook_post_config: Place a hook that executes after configuration has been parsed, but before Apache has forkedap_hook_translate_name: Place a hook that executes when a URI needs to be translated into a filename on the server (think mod_rewrite)Building a handler
+A simple "Hello, world!" handler
+Let's start off by making a very simple request handler that does the following:
+
+
+In C code, our example handler will now look like this:text/html
+
+
+
+
+
+
+
+
+Now, we put all we have learned together and end up with a program that looks like
+mod_example_1.c. The functions used in this example will be explained later in the section
+"Some useful functions you should know".
+
+static int example_handler(request_rec *r)
+{
+ /* First off, we need to check if this is a call for the "example-handler" handler.
+ * If it is, we accept it and do our things, it not, we simply return DECLINED,
+ * and Apache will try somewhere else.
+ */
+ if (!r->handler || strcmp(r->handler, "example-handler")) return (DECLINED);
+
+ /* Now that we are handling this request, we'll write out "Hello, world!" to the client.
+ * To do so, we must first set the appropriate content type, followed by our output.
+ */
+ ap_set_content_type(r, "text/html");
+ ap_rprintf(r, "Hello, world!");
+
+ /* Lastly, we must tell Apache that we took care of this request and everything went fine.
+ * We do so by simply returning the value OK to Apache.
+ */
+ return OK;
+}
+The request_rec structure
+request_req* structure passed along with every call that is made. This
+struct, typically just refered to as r in modules, contains all the information you need for
+your module to fully process any HTTP request and respond accordingly.request_req structure are:
+
+
+A complete list of all the values contained with in the : Contains the name of the handler, Apache is currently asking to do the handling of this requestr->handler (char*): Contains the HTTP method being used, f.x. GET or POSTr->method (char*): Contains the translated filename the client is requestingr->filename (char*): Contains the query string of the request, if anyr->args (char*): Contains all the headers sent by the clientr->headers_in (apr_table_t*): A record containing information about the current connectionr->connection (conn_rec*): The IP address of the client connecting to usr->useragent_ip (char*): The memory pool of this request. We'll discuss this in the "
+Memory management" chapter.r->pool (apr_pool_t*)request_req structure can be found in
+the httpd.h header
+file or at [insert link here].
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int example_handler(request_rec *r)
+{
+ /* Set the appropriate content type */
+ ap_set_content_type(r, "text/html");
+
+ /* Print out the IP address of the client connecting to us: */
+ ap_rprintf(r, "<h2>Hello, %s!</h2>", r->useragent_ip);
+
+ /* If we were reached through a GET or a POST request, be happy, else sad. */
+ if ( !strcmp(r->method, "POST") || !strcmp(r->method, "GET") ) {
+ ap_rputs("You used a GET or a POST method, that makes us happy!<br>", r);
+ }
+ else {
+ ap_rputs("You did not use POST or GET, that makes us sad :(<br>", r);
+ }
+
+ /* Lastly, if there was a query string, let's print that too! */
+ if (r->args) {
+ ap_rprintf(r, "Your query string was: %s", r->args);
+ }
+ return OK;
+}
+Return values
+DECLINED. If it is handling
+a request, it should either return the generic value OK, or a specific HTTP status
+code, for example:
+
+
+
+
+
+
+
+
+Returning static int example_handler(request_rec *r)
+{
+ /* Return 404: Not found */
+ return HTTP_NOT_FOUND;
+}
+OK or a HTTP status code does not necessarilly mean that the request will end. Apache
+may still have other handlers that are interested in this request, for instance the logging modules
+which, upon a successful request, will write down a summary of what was requested and how it went.
+To do a full stop and prevent any further processing after your module is done, you can return the
+value DONE to let Apache know that it should cease all activity on this request and
+carry on with the next, without informing other handlers.
+
+General response codes:
+
+
DECLINED: We are not handling this requestOK: We handled this request and it went wellDONE: We handled this request and Apache should just close this thread without further processing
+HTTP specific return codes (excerpt):
+
+
+
+
+
+HTTP_OK (200): Request was okayHTTP_MOVED_PERMANENTLY (301): The resource has moved to a new URLHTTP_UNAUTHORIZED (401): Client is not authorized to visit this pageHTTP_FORBIDDEN (403): Permission deniedHTTP_NOT_FOUND (404): File not foundHTTP_INTERNAL_SERVER_ERROR (500): Internal server error (self explanatory)Some useful functions you should know
+
+
+
+
+
+ap_rputs(const char *string, request_req *r):
+ Sends a string of text to the client. This is a shorthand version of
+ ap_rwrite.
+
+
+
+ap_rputs
+
+
+
+("Hello, world!", r);
+ ap_rprintf:
+ This function works just like printf, except it sends the result to the client.
+
+
+
+
+
+
+ap_rprintf(r, "Hello, %s!", r->useragent_ip);
+ ap_set_content_type(request_req *r, const char *type):
+ Sets the content type of the output you are sending.
+
+
+
+
+
+
+ap_set_content_type(r, "text/plain"); /* force a raw text output */Memory management
+ reference when creating new objects. A few of the functions for
+allocating memory within a pool are:
+r->pool
+
+Let's put these functions into an example handler:void* apr_palloc(
+apr_pool_t *p, apr_size_t size): Allocates size number of bytes in the pool for youvoid* apr_pcalloc(
+apr_pool_t *p, apr_size_t size): Allocates size number of bytes in the pool for you and sets all bytes to 0char* apr_pstrdup(
+apr_pool_t *p, const char *s): Creates a duplicate of the string s. This is useful for copying constant values so you can edit them
+
+
+
+
+
+
+
+This is all well and good for our module, which won't need any pre-initialized variables or structures.
+However, if we wanted to initialize something early on, before the requests come rolling in, we could
+simply add a call to a function in our static int example_handler(request_rec *r)
+{
+ const char* original = "You can't edit this!";
+ /* Allocate space for 10 integer values and set them all to zero. */
+ int* integers = apr_pcalloc(r->pool, sizeof(int)*10);
+
+ /* Create a copy of the 'original' variable that we can edit. */
+ char* copy = apr_pstrdup(r->pool, original);
+ return OK;
+}
+register_hooks function to sort it out:
+
+
+
+
+
+
+In this, pre-request initialization function, we would not be using the same pool as we did when
+allocating resources for request-based functions. Instead, we would use the pool given to us by
+Apache for allocating memory on a per-process based level.
+
+
+
+
+static void register_hooks(apr_pool_t *pool)
+{
+ /* Call a function that initializes some stuff */
+ example_init_function(pool);
+ /* Create a hook in the request handler, so we get called when a request arrives */
+ ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
+}
+Parsing request data
+valueA=yes&valueB=no&valueC=maybe. It is up to the module itself to parse these
+and get the data it requires. In our example, we'll be looking for a key called digest,
+and if set to md5, we'll produce an MD5 digest, otherwise we'll produce a SHA1 digest.
+
+apr_table_t *GET;
+apr_array_header_t *POST;
+
+ap_args_to_table(r, &GET);
+ap_parse_form_data(r,
+
+
+
+In our specific example module, we're looking for the NULL, &POST, -1, 8192);
+digest value from the query string,
+which now resides inside a table called GET. To extract this value, we need only perform
+a simple operation:
+
+
+
+
+
+
+
+
+The structures used for the POST and GET data are not exactly the same, so if we were to fetch a value from
+POST data instead of the query string, we would have to resort to a few more lines, as outlined in
+this example in the last chapter of this document.
+
+
+
+/* Get the "digest" key from the query string, if any. */
+const char *digestType = apr_table_get(GET, "digest");
+
+/* If no key was returned, we will set a default value instead. */
+if (!digestType) digestType = "sha1";
+
+Making an advanced handler
+Now that we have learned how to parse form data and manage our resources, we can move on to creating an advanced
+version of our module, that spits out the MD5 or SHA1 digest of files:
+
+
+
+
+
+
+
+
+This version in its entirity can be found here: mod_example_2.c.
+
+
+static int example_handler(request_rec *r)
+{
+ int rc, exists;
+ apr_finfo_t finfo;
+ apr_file_t *file;
+ char *filename;
+ char buffer[256];
+ apr_size_t readBytes;
+ int n;
+ apr_table_t *GET;
+ apr_array_header_t *POST;
+ const char *digestType;
+
+
+ /* Check that the "example-handler" handler is being called. */
+ if (!r->handler || strcmp(r->handler, "example-handler")) return (DECLINED);
+
+ /* Figure out which file is being requested by removing the .sum from it */
+ filename = apr_pstrdup(r->pool, r->filename);
+ filename[strlen(filename)-4] = 0; /* Cut off the last 4 characters. */
+
+ /* Figure out if the file we request a sum on exists and isn't a directory */
+ rc = apr_stat(&finfo, filename, APR_FINFO_MIN, r->pool);
+ if (rc == APR_SUCCESS) {
+ exists =
+ (
+ (finfo.filetype != APR_NOFILE)
+ && !(finfo.filetype & APR_DIR)
+ );
+ if (!exists) return HTTP_NOT_FOUND; /* Return a 404 if not found. */
+ }
+ /* If apr_stat failed, we're probably not allowed to check this file. */
+ else return HTTP_FORBIDDEN;
+
+ /* Parse the GET and, optionally, the POST data sent to us */
+
+ ap_args_to_table(r, &GET);
+ ap_parse_form_data(r, NULL, &POST, -1, 8192);
+
+ /* Set the appropriate content type */
+ ap_set_content_type(r, "text/html");
+
+ /* Print a title and some general information */
+ ap_rprintf(r, "<h2>Information on %s:</h2>", filename);
+ ap_rprintf(r, "<b>Size:</b> %u bytes<br/>", finfo.size);
+
+ /* Get the digest type the client wants to see */
+ digestType = apr_table_get(GET, "digest");
+ if (!digestType) digestType = "MD5";
+
+
+ rc = apr_file_open(&file, filename, APR_READ, APR_OS_DEFAULT, r->pool);
+ if (rc == APR_SUCCESS) {
+
+ /* Are we trying to calculate the MD5 or the SHA1 digest? */
+ if (!strcasecmp(digestType, "md5")) {
+ /* Calculate the MD5 sum of the file */
+ union {
+ char chr[16];
+ uint32_t num[4];
+ } digest;
+ apr_md5_ctx_t md5;
+ apr_md5_init(&md5);
+ readBytes = 256;
+ while ( apr_file_read(file, buffer, &readBytes) == APR_SUCCESS ) {
+ apr_md5_update(&md5, buffer, readBytes);
+ }
+ apr_md5_final(digest.chr, &md5);
+
+ /* Print out the MD5 digest */
+ ap_rputs("<b>MD5: </b><code>", r);
+ for (n = 0; n < APR_MD5_DIGESTSIZE/4; n++) {
+ ap_rprintf(r, "%08x", digest.num[n]);
+ }
+ ap_rputs("</code>", r);
+ /* Print a link to the SHA1 version */
+ ap_rputs("<br/><a href='?digest=sha1'>View the SHA1 hash instead</a>", r);
+ }
+ else {
+ /* Calculate the SHA1 sum of the file */
+ union {
+ char chr[20];
+ uint32_t num[5];
+ } digest;
+ apr_sha1_ctx_t sha1;
+ apr_sha1_init(&sha1);
+ readBytes = 256;
+ while ( apr_file_read(file, buffer, &readBytes) == APR_SUCCESS ) {
+ apr_sha1_update(&sha1, buffer, readBytes);
+ }
+ apr_sha1_final(digest.chr, &sha1);
+
+ /* Print out the SHA1 digest */
+ ap_rputs("<b>SHA1: </b><code>", r);
+ for (n = 0; n < APR_SHA1_DIGESTSIZE/4; n++) {
+ ap_rprintf(r, "%08x", digest.num[n]);
+ }
+ ap_rputs("</code>", r);
+
+ /* Print a link to the MD5 version */
+ ap_rputs("<br/><a href='?digest=md5'>View the MD5 hash instead</a>", r);
+ }
+ apr_file_close(file);
+
+ }
+
+
+
+ /* Let Apache know that we responded to this request. */
+ return OK;
+}
+Adding configuration options
+An introduction to configuration directives
+If you are reading this, then you probably already know what a configuration directive is. Simply put,
+a directive is a way of telling an individual module (or a set of modules) how to behave, such as these
+directives control how mod_rewrite works:
+
+RewriteEngine On
+RewriteCond %{REQUEST_URI} ^/foo/bar
+RewriteRule ^/foo/bar/(.*)$ /foobar?page=$1
+Making an example configuration
+To begin with, we'll create a basic configuration in C-space:
+
+
+
+
+
+
+
+
+Now, let's put this into perspective by creating a very small module that just prints out a hard-coded
+configuration. You'll notice that we use the typedef struct {
+ int enabled; /* Enable or disable our module */
+ const char *path; /* Some path to...something */
+ int typeOfAction; /* 1 means action A, 2 means action B and so on */
+} example_config;
+register_hooks function for initializing the
+configuration values to their defaults:
+
+
+
+
+
+
+
+
+So far so good. To access our new handler, we could add the following to our configuration:
+typedef struct {
+ int enabled; /* Enable or disable our module */
+ const char *path; /* Some path to...something */
+ int typeOfAction; /* 1 means action A, 2 means action B and so on */
+} example_config;
+
+static example_config config;
+
+static int example_handler(request_rec *r)
+{
+ if (!r->handler || strcmp(r->handler, "example-handler")) return(DECLINED);
+ ap_set_content_type(r, "text/plain");
+ ap_rprintf(r, "Enabled: %u\n", config.enabled);
+ ap_rprintf(r, "Path: %s\n", config.path);
+ ap_rprintf(r, "TypeOfAction: %x\n", config.typeOfAction);
+ return OK;
+}
+
+static void register_hooks(apr_pool_t *pool)
+{
+ config.enabled = 1;
+ config.path = "/foo/bar";
+ config.typeOfAction = 0x00;
+ ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
+}
+
+/* Define our module as an entity and assign a function for registering hooks */
+
+module AP_MODULE_DECLARE_DATA example_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL, /* Per-directory configuration handler */
+ NULL, /* Merge handler for per-directory configurations */
+ NULL, /* Per-server configuration handler */
+ NULL, /* Merge handler for per-server configurations */
+ NULL, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+
+<Location /example>
+ SetHandler example-handler
+</Location>
+
Registering directives with Apache
+What if we want to change our configuration, not by hard-coding new values into the module,
+but by using either the httpd.conf file or possibly a .htaccess file? It's time to let Apache
+know that we want this to be possible. To do so, we must first change our name tag
+to include a reference to the configuration directives we want to register with Apache:
+
+
+
+
+module AP_MODULE_DECLARE_DATA example_module =
+{
+ STANDARD20_MODULE_STUFF,
+
+
+
+
+This will tell Apache that we are now accepting directives from the configuration files, and that the
+structure called NULL, /* Per-directory configuration handler */
+ NULL, /* Merge handler for per-directory configurations */
+ NULL, /* Per-server configuration handler */
+ NULL, /* Merge handler for per-server configurations */
+ example_directives, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+example_directives holds information on what our directives are and how
+they work. Since we have three different variables in our module configuration, we will add a structure
+with three directives and a NULL at the end:
+
+
+
+
+static const command_rec example_directives[] =
+{
+
+
+
+
+AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, RSRC_CONF, "Enable or disable mod_example"),
+ AP_INIT_TAKE1("examplePath", example_set_path, NULL, RSRC_CONF, "The path to whatever"),
+ AP_INIT_TAKE2("exampleAction", example_set_action, NULL, RSRC_CONF, "Special action value!"),
+ { NULL }
+};
+
+
+
+(The "missing" parameter in our definition, which is usually set to AP_INIT_TAKE1: This is a macro that tells Apache that this directive takes one and only one argument.
+If we required two arguments, we could use the macro AP_INIT_TAKE2 and so on (refer to httpd_conf.h
+for more macros).exampleEnabled: This is the name of our directive. More precisely, it is what the user must put in his/her
+configuration in order to invoke a configuration change in our module.example_set_enabled: This is a reference to a C function that parses the directive and sets the configuration
+accordingly. We will discuss how to make this in the following paragraph.RSRC_CONF: This tells Apache where the directive is permissable. We'll go into details on this value in the
+later chapters, but for now, RSRC_CONF means that Apache will only accept these directives in a server context."Enable or disable....": This is simply a brief description of what the directive does.NULL, is an optional function that can be
+run after the initial function to parse the arguments have been run. This is usually omitted, as the function for verifying
+arguments might as well be used to set them.)
+
+
+The directive handler function
+exampleAction
+directive to accept two arguments, its C function also has an additional parameter defined:
+
+
+
+
+
+
+
+
+
+
+/* Handler for the "exambleEnabled" directive */
+const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ if(!strcasecmp(arg, "on")) config.enabled = 1;
+ else config.enabled = 0;
+ return NULL;
+}
+
+/* Handler for the "examplePath" directive */
+const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ config.path = arg;
+ return NULL;
+}
+
+/* Handler for the "exampleAction" directive */
+/* Let's pretend this one takes one argument (file or db), and a second (deny or allow), */
+/* and we store it in a bit-wise manner. */
+const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char* arg2)
+{
+ if(!strcasecmp(arg1, "file")) config.typeOfAction = 0x01;
+ else config.typeOfAction = 0x02;
+
+ if(!strcasecmp(arg2, "deny")) config.typeOfAction += 0x10;
+ else config.typeOfAction += 0x20;
+ return NULL;
+}
+Putting it all together
+
+
+
+
+
+
+/* mod_example_config_simple.c: */
+#include <stdio.h>
+#include "apr_hash.h"
+#include "ap_config.h"
+#include "ap_provider.h"
+#include "httpd.h"
+#include "http_core.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+
+/*
+ ==============================================================================
+ Our configuration prototype and declaration:
+ ==============================================================================
+ */
+typedef struct {
+ int enabled; /* Enable or disable our module */
+ const char *path; /* Some path to...something */
+ int typeOfAction; /* 1 means action A, 2 means action B and so on */
+} example_config;
+
+static example_config config;
+
+/*
+ ==============================================================================
+ Our directive handlers:
+ ==============================================================================
+ */
+/* Handler for the "exambleEnabled" directive */
+const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ if(!strcasecmp(arg, "on")) config.enabled = 1;
+ else config.enabled = 0;
+ return NULL;
+}
+
+/* Handler for the "examplePath" directive */
+const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ config.path = arg;
+ return NULL;
+}
+
+/* Handler for the "exampleAction" directive */
+/* Let's pretend this one takes one argument (file or db), and a second (deny or allow), */
+/* and we store it in a bit-wise manner. */
+const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char* arg2)
+{
+ if(!strcasecmp(arg1, "file")) config.typeOfAction = 0x01;
+ else config.typeOfAction = 0x02;
+
+ if(!strcasecmp(arg2, "deny")) config.typeOfAction += 0x10;
+ else config.typeOfAction += 0x20;
+ return NULL;
+}
+
+/*
+ ==============================================================================
+ The directive structure for our name tag:
+ ==============================================================================
+ */
+static const command_rec example_directives[] =
+{
+ AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, RSRC_CONF, "Enable or disable mod_example"),
+ AP_INIT_TAKE1("examplePath", example_set_path, NULL, RSRC_CONF, "The path to whatever"),
+ AP_INIT_TAKE2("exampleAction", example_set_action, NULL, RSRC_CONF, "Special action value!"),
+ { NULL }
+};
+/*
+ ==============================================================================
+ Our module handler:
+ ==============================================================================
+ */
+static int example_handler(request_rec *r)
+{
+ if(!r->handler || strcmp(r->handler, "example-handler")) return(DECLINED);
+ ap_set_content_type(r, "text/plain");
+ ap_rprintf(r, "Enabled: %u\n", config.enabled);
+ ap_rprintf(r, "Path: %s\n", config.path);
+ ap_rprintf(r, "TypeOfAction: %x\n", config.typeOfAction);
+ return OK;
+}
+
+/*
+ ==============================================================================
+ The hook registration function (also initializes the default config values):
+ ==============================================================================
+ */
+static void register_hooks(apr_pool_t *pool)
+{
+ config.enabled = 1;
+ config.path = "/foo/bar";
+ config.typeOfAction = 3;
+ ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
+}
+/*
+ ==============================================================================
+ Our module name tag:
+ ==============================================================================
+ */
+module AP_MODULE_DECLARE_DATA example_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL, /* Per-directory configuration handler */
+ NULL, /* Merge handler for per-directory configurations */
+ NULL, /* Per-server configuration handler */
+ NULL, /* Merge handler for per-server configurations */
+ example_directives, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+
+ExampleEnabled On
+ExamplePath "/usr/bin/foo"
+ExampleAction file allow
+
/example on our web site, and we see the configuration has
+adapted to what we wrote in our configuration file.
+
+
+
+
+Context aware configurations
+Introduction to context aware configurations
+
+<Directory "/var/www">
+ RewriteCond %{HTTP_HOST} ^example.com$
+ RewriteRule (.*) http://www.example.com/$1
+</Directory>
+<Directory "/var/www/sub">
+ RewriteRule ^foobar$ index.php?foobar=true
+</Directory>
+
+
+If mod_rewrite (or Apache for that matter) wasn't context aware, then these rewrite rules would just apply to every and any request made,
+regardless of where and how they were made, but since the module can pull the context specific configuration straight from Apache, it
+does not need to know itself, which of the directives are valid in this context, since Apache takes care of this.
+
+/var/www, all requests for http://example.com must go to http://www.example.com/var/www/sub, all requests for foobar must go to index.php?foobar=true
+
+
+
+example_config *config = (example_config*) ap_get_module_config(
+That's it! Of course, a whole lot goes on behind the scenes, which we will discuss in this chapter, starting with how
+Apache came to know what our configuration looks like, and how it came to be set up as it is in the specific context.
+
+
+
+r->per_dir_config, &example_module);
+Our basic configuration setup
+context variable that we can use to track
+which context configuration is being used by Apache in various places:
+
+
+
+
+
+
+
+
+
+
+typedef struct {
+ char context[256];
+ char path[256];
+ int typeOfAction;
+ int enabled;
+} example_config;
+
+
+
+
+
+
+
+
+
+static int example_handler(request_rec *r)
+{
+ if(!r->handler || strcmp(r->handler, "example-handler")) return(DECLINED);
+ example_config *config = (example_config*) ap_get_module_config(r->per_dir_config, &example_module);
+ ap_set_content_type(r, "text/plain");
+ ap_rprintf("Enabled: %u\n", config->enabled);
+ ap_rprintf("Path: %s\n", config->path);
+ ap_rprintf("TypeOfAction: %x\n", config->typeOfAction);
+ ap_rprintf("Context: %s\n", config->context);
+ return OK;
+}
+Choosing a context
+
+
+
+
+
+The AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, RSRC_CONF, "Enable or disable mod_example"),
+RSRC_CONF definition told Apache that we would only allow this directive in a global server context, but
+since we are now trying out a context aware version of our module, we should set this to something more lenient, namely
+the value ACCESS_CONF, which lets us use the directive inside <Directory> and <Location> blocks.
+
+
+
+Using Apache to allocate configuration slots
+
+module AP_MODULE_DECLARE_DATA example_module
+
+
+
+
+
+
+
+
+
+=
+{
+ STANDARD20_MODULE_STUFF,
+ create_dir_conf, /* Per-directory configuration handler */
+ merge_dir_conf, /* Merge handler for per-directory configurations */
+ NULL, /* Per-server configuration handler */
+ NULL, /* Merge handler for per-server configurations */
+ directives, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+Creating new context configurations
+
+
+
+
+
+
+
+
+void* example_create_dir_conf(apr_pool_t* pool, char* context) {
+ context = context ? context : "(undefined context)";
+ example_config *cfg = apr_pcalloc(pool, sizeof(example_config));
+ if(cfg) {
+ /* Set some default values */
+ strcpy(cfg->context, x);
+ cfg->enabled = 0;
+ cfg->path = "/foo/bar";
+ cfg->typeOfAction = 0x11;
+ }
+ return dir;
+}
+Merging configurations
+
+<Directory "/var/www">
+ ExampleEnable On
+ ExamplePath /foo/bar
+ ExampleAction file allow
+</Directory>
+<Directory "/var/www/subdir">
+ ExampleAction file deny
+</Directory>
+
/var/www/subdir should inherit the
+value set for the /var/www directory, as we did not specify a ExampleEnable nor an
+ExamplePath for this directory. Apache does not presume to know if this is true, but cleverly
+does the following:
+
+
+This proposal is handled by the /var/www/var/www/var/www/subdir/var/www/subdir/var/www/subdirmerge_dir_conf function we referenced in our name tag. The purpose of
+this function is to assess the two configurations and decide how they are to be merged:
+
+
+
+
+
+
+
+
+
+
+void* merge_dir_conf(apr_pool_t* pool, void* BASE, void* ADD) {
+ example_config* base = BASE ;
+ example_config* add = ADD ;
+ example_config* conf = create_dir_conf(pool, "Merged configuration");
+
+ conf->enabled = ( add->enabled == 0 ) ? base->enabled : add->enabled ;
+ conf->typeOfAction = add->typeOfAction ? add->typeOfAction : base->typeOfAction;
+ strcpy(conf->path, strlen(add->path) ? add->path : base->path);
+
+ return conf ;
+}
+Trying out our new context aware configurations
+
+<Location "/a">
+ SetHandler example-handler
+ ExampleEnabled on
+ ExamplePath "/foo/bar"
+ ExampleAction file allow
+</Location>
+
+<Location "/a/b">
+ ExampleAction file deny
+ ExampleEnabled off
+</Location>
+
+<Location "/a/b/c">
+ ExampleAction db deny
+ ExamplePath "/foo/bar/baz"
+ ExampleEnabled on
+</Location>
+
+
+
+
+
+
+
+
+/*$6
+ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ * mod_example_config.c
+ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ */
+
+
+#include <stdio.h>
+#include "apr_hash.h"
+#include "ap_config.h"
+#include "ap_provider.h"
+#include "httpd.h"
+#include "http_core.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+
+/*$1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Configuration structure
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+typedef struct
+{
+ char context[256];
+ char path[256];
+ int typeOfAction;
+ int enabled;
+} example_config;
+
+/*$1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Prototypes
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+static int example_handler(request_rec *r);
+const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg);
+const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg);
+const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char *arg2);
+void *create_dir_conf(apr_pool_t *pool, char *context);
+void *merge_dir_conf(apr_pool_t *pool, void *BASE, void *ADD);
+static void register_hooks(apr_pool_t *pool);
+
+/*$1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Configuration directives
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+static const command_rec directives[] =
+{
+ AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, ACCESS_CONF, "Enable or disable mod_example"),
+ AP_INIT_TAKE1("examplePath", example_set_path, NULL, ACCESS_CONF, "The path to whatever"),
+ AP_INIT_TAKE2("exampleAction", example_set_action, NULL, ACCESS_CONF, "Special action value!"),
+ { NULL }
+};
+
+/*$1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Our name tag
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+module AP_MODULE_DECLARE_DATA example_module =
+{
+ STANDARD20_MODULE_STUFF,
+ create_dir_conf, /* Per-directory configuration handler */
+ merge_dir_conf, /* Merge handler for per-directory configurations */
+ NULL, /* Per-server configuration handler */
+ NULL, /* Merge handler for per-server configurations */
+ directives, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+
+/*
+ =======================================================================================================================
+ Hook registration function
+ =======================================================================================================================
+ */
+static void register_hooks(apr_pool_t *pool)
+{
+ ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
+}
+
+/*
+ =======================================================================================================================
+ Our example web service handler
+ =======================================================================================================================
+ */
+static int example_handler(request_rec *r)
+{
+ if(!r->handler || strcmp(r->handler, "example-handler")) return(DECLINED);
+
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ example_config *config = (example_config *) ap_get_module_config(r->per_dir_config, &example_module);
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ ap_set_content_type(r, "text/plain");
+ ap_rprintf(r, "Enabled: %u\n", config->enabled);
+ ap_rprintf(r, "Path: %s\n", config->path);
+ ap_rprintf(r, "TypeOfAction: %x\n", config->typeOfAction);
+ ap_rprintf(r, "Context: %s\n", config->context);
+ return OK;
+}
+
+/*
+ =======================================================================================================================
+ Handler for the "exambleEnabled" directive
+ =======================================================================================================================
+ */
+const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ example_config *conf = (example_config *) cfg;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ if(conf)
+ {
+ if(!strcasecmp(arg, "on"))
+ conf->enabled = 1;
+ else
+ conf->enabled = 0;
+ }
+
+ return NULL;
+}
+
+/*
+ =======================================================================================================================
+ Handler for the "examplePath" directive
+ =======================================================================================================================
+ */
+const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ example_config *conf = (example_config *) cfg;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ if(conf)
+ {
+ strcpy(conf->path, arg);
+ }
+
+ return NULL;
+}
+
+/*
+ =======================================================================================================================
+ Handler for the "exampleAction" directive ;
+ Let's pretend this one takes one argument (file or db), and a second (deny or allow), ;
+ and we store it in a bit-wise manner.
+ =======================================================================================================================
+ */
+const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char *arg2)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ example_config *conf = (example_config *) cfg;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ if(conf)
+ {
+ {
+ if(!strcasecmp(arg1, "file"))
+ conf->typeOfAction = 0x01;
+ else
+ conf->typeOfAction = 0x02;
+ if(!strcasecmp(arg2, "deny"))
+ conf->typeOfAction += 0x10;
+ else
+ conf->typeOfAction += 0x20;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ =======================================================================================================================
+ Function for creating new configurations for per-directory contexts
+ =======================================================================================================================
+ */
+void *create_dir_conf(apr_pool_t *pool, char *context)
+{
+ context = context ? context : "Newly created configuration";
+
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ example_config *cfg = apr_pcalloc(pool, sizeof(example_config));
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ if(cfg)
+ {
+ {
+ /* Set some default values */
+ strcpy(cfg->context, context);
+ cfg->enabled = 0;
+ memset(cfg->path, 0, 256);
+ cfg->typeOfAction = 0x00;
+ }
+ }
+
+ return cfg;
+}
+
+/*
+ =======================================================================================================================
+ Merging function for configurations
+ =======================================================================================================================
+ */
+void *merge_dir_conf(apr_pool_t *pool, void *BASE, void *ADD)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ example_config *base = BASE;
+ example_config *add = ADD;
+ example_config *conf = create_dir_conf(pool, "Merged configuration");
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ conf->enabled = (add->enabled == 0) ? base->enabled : add->enabled;
+ conf->typeOfAction = add->typeOfAction ? add->typeOfAction : base->typeOfAction;
+ strcpy(conf->path, strlen(add->path) ? add->path : base->path);
+ return conf;
+}
+Summing up
+Some useful snippets of code
+
+Retrieve a variable from POST form data
+
+
+
+
+
+
+
+
+
+
+ const char *read_post_value(const char *key)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ const apr_array_header_t *fields;
+ int i;
+ apr_table_entry_t *e = 0;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ e = (apr_table_entry_t *) fields->elts;
+ for(i = 0; i < fields->nelts; i++) {
+ if(!strcmp(e[i].key, key)) return e[i].val;
+ }
+ return 0;
+}
+static int example_handler(request_req *r)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~*/
+ apr_array_header_t *POST;
+ const char *value;
+ /*~~~~~~~~~~~~~~~~~~~~~~*/
+ ap_parse_form_data(r, NULL, &POST, -1, 8192);
+
+ value = read_post_value(POST, "valueA");
+ if (!value) value = "(undefined)";
+ ap_rprintf(r, "The value of valueA is: %s", value);
+ return OK;
+}
+ Printing out every HTTP header received
+
+
+
+
+
+
+
+
+
+
+ static int example_handler(request_req *r)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ const apr_array_header_t *fields;
+ int i;
+ apr_table_entry_t *e = 0;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ fields = apr_table_elts(r->headers_in);
+ e = (apr_table_entry_t *) fields->elts;
+ for(i = 0; i < fields->nelts; i++) {
+ ap_rprintf(r, "<b>%s</b>: %s<br/>", e[i].key, e[i].val);
+ }
+ return OK;
+}
+ Reading the request body into memory
+
+
+
+
+
+
+
+
+
+
+static int util_read(request_rec *r, const char **rbuf, apr_off_t *size)
+{
+ /*~~~~~~~~*/
+ int rc = OK;
+ /*~~~~~~~~*/
+
+ if((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
+ return(rc);
+ }
+
+ if(ap_should_client_block(r)) {
+
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ char argsbuffer[HUGE_STRING_LEN];
+ apr_off_t rsize, len_read, rpos = 0;
+ apr_off_t length = r->remaining;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ *rbuf = (const char *) apr_pcalloc(r->pool, (apr_size_t) (length + 1));
+ *size = length;
+ while((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) {
+ if((rpos + len_read) > length) {
+ rsize = length - rpos;
+ }
+ else {
+ rsize = len_read;
+ }
+
+ memcpy((char *) *rbuf + rpos, argsbuffer, (size_t) rsize);
+ rpos += rsize;
+ }
+ }
+ return(rc);
+}
+
+static int example_handler(request_req* r)
+{
+ /*~~~~~~~~~~~~~~~~*/
+ apr_off_t size;
+ const char *buffer;
+ /*~~~~~~~~~~~~~~~~*/
+
+ if(util_read(r, &data, &size) == OK) {
+ ap_rprintf("We read a request body that was %u bytes long", size);
+ }
+ return OK;
+}
+ mod_example. In the first part of this document, the purpose of this
+module will be to calculate and print out various digest values for existing files on your web server, whenever we
+access the URL http://hostname/filename.sum. For instance, if we want to know the MD5 digest value of the file
+located at http://www.example.com/index.html, we would visit http://www.example.com/index.html.sum.
+
+apxs -i -a -c mod_example.c
+

+
+module AP_MODULE_DECLARE_DATA example_module
+
+
+
+This bit of code lets Apache know that we have now registered a new module in the system,
+and that its name is =
+{
+ STANDARD20_MODULE_STUFF,
+ create_dir_conf, /* Per-directory configuration handler */
+ merge_dir_conf, /* Merge handler for per-directory configurations */
+ create_svr_conf, /* Per-server configuration handler */
+ merge_svr_conf, /* Merge handler for per-server configurations */
+ directives, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+example_module. The name of the module is used primarilly
+for two things:
+
+
+For now, we're only concerned with the first purpose of the module name,
+which comes into play when we need to load the module:
+LoadModule example_module modules/mod_example.so
mod_example.so and look for a module
+called example_module.
+
+Within this name tag of ours is also a bunch of references to how we would like to handle things: +Which directives do we respond to in a configuration file or .htaccess, how do we operate +within specific contexts, and what handlers are we interested in registering with the Apache +service. We'll return to all these elements later in this document. +
+ +
+When handling requests in Apache, the first thing you will need to do is create a hook into
+the request handling process. A hook is essentially a message telling Apache that you are
+willing to either serve or at least take a glance at certain requests given by clients. All
+handlers, whether it's mod_rewrite, mod_authn_*, mod_proxy and so on, are hooked into specific
+parts of the request process. As you are probably aware, modules serve different purposes; Some
+are authentication/authorization handlers, others are file or script handlers while some third
+modules rewrite URIs or proxies content. Furthermore, in the end, it is up to the user of Apache
+how and when each module will come into place. Thus, Apache itself does not presume to know
+which module is responsible for handling a specific request, and will ask each module whether
+they have an interest in a given request or not. It is then up to each module to either gently
+decline serving a request, accept serving it or flat out deny the request from being served,
+as authentication/authorization modules do:
+
+To make it a bit easier for handlers such as our mod_example to know whether the client is
+requesting content we should handle or not, Apache has directives for hinting to modules whether
+their assistance is needed or not. Two of these are mod_example,
+so we'll add a configuration directive that tells Apache to do just that:
+
+AddHandler example-handler .sum
+
AddHandler and
+reply to Apache based on the value of this tag.
+
+To begin with, we only want to create a simple handler, that replies to the client browser
+when a specific URL is requested, so we won't bother setting up configuration handlers and
+directives just yet. Our initial module definition will look like this:
+
+
+
+
+module AP_MODULE_DECLARE_DATA example_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ register_hooks /* Our hook registering function */
+};
+
+
+
+
+This lets Apache know that we are not interesting in anything fancy, we just want to hook onto
+the requests and possibly handle some of them.
+
+
+The reference in our example declaration, register_hooks is the name of a function
+we will create to manage how we hook onto the request process. In this example module, the function
+has just one purpose; To create a simple hook that gets called after all the rewrites, access
+control etc has been handled. Thus, we will let Apache know, that we want to hook into its process
+as one of the last modules:
+
+
+
+
++ + + +Thestaticvoidregister_hooks(apr_pool_t*pool)+{+/* Create a hook in the request handler, so we get called when a request arrives */+ap_hook_handler(example_handler,NULL,NULL,APR_HOOK_LAST);+}+
example_handler reference is the function that will handle the request. We will
+discuss how to create a handler in the next chapter.
+
++Hooking into the request handling phase is but one of many hooks that you can create. Some other ways of hooking are: +
ap_hook_child_init: Place a hook that executes when a child process is spawned (commonly used for initializing modules after Apache has forked)ap_hook_pre_config: Place a hook that executes before any configuration data has been read (very early hook)ap_hook_post_config: Place a hook that executes after configuration has been parsed, but before Apache has forkedap_hook_translate_name: Place a hook that executes when a URI needs to be translated into a filename on the server (think mod_rewrite)+A handler is essentially a function that receives a callback when a request to Apache is made. +It is passed a record of the current request (how it was made, which headers and requests were +passed along, who's giving the request and so on), and is put in charge of either telling +Apache that it's not interested in the request or handle the request with the tools provided. +
+text/html++ + + +Now, we put all we have learned together and end up with a program that looks like +mod_example_1.c. The functions used in this example will be explained later in the section +"Some useful functions you should know". +staticintexample_handler(request_rec*r)+{+/* First off, we need to check if this is a call for the "example-handler" handler.+* If it is, we accept it and do our things, it not, we simply return DECLINED,+* and Apache will try somewhere else.+*/+if(!r->handler||strcmp(r->handler,"example-handler"))return(DECLINED);+ +/* Now that we are handling this request, we'll write out "Hello, world!" to the client.+* To do so, we must first set the appropriate content type, followed by our output.+*/+ ap_set_content_type(r,"text/html");+ ap_rprintf(r,"Hello, world!");+ +/* Lastly, we must tell Apache that we took care of this request and everything went fine.+* We do so by simply returning the value OK to Apache.+*/+returnOK;+}+
The most essential part of any request is the request record. In a call to a handler function, this
+is represented by the request_req* structure passed along with every call that is made. This
+struct, typically just refered to as r in modules, contains all the information you need for
+your module to fully process any HTTP request and respond accordingly.
Some key elements of the request_req structure are:
+
r->handler (char*): Contains the name of the handler, Apache is currently asking to do the handling of this requestr->method (char*): Contains the HTTP method being used, f.x. GET or POSTr->filename (char*): Contains the translated filename the client is requestingr->args (char*): Contains the query string of the request, if anyr->headers_in (apr_table_t*): Contains all the headers sent by the clientr->connection (conn_rec*): A record containing information about the current connectionr->useragent_ip (char*): The IP address of the client connecting to usr->pool (apr_pool_t*): The memory pool of this request. We'll discuss this in the "
+Memory management" chapter.request_req structure can be found in
+the httpd.h header
+file or at [insert link here].
+
+
+Let's try out some of these variables in another example handler:
+
+
+
+
++ + + + +staticintexample_handler(request_rec*r)+{+/* Set the appropriate content type */+ ap_set_content_type(r,"text/html");+ +/* Print out the IP address of the client connecting to us: */+ ap_rprintf(r,"<h2>Hello,%s!</h2>",r->useragent_ip);+ +/* If we were reached through a GET or a POST request, be happy, else sad. */+if(!strcmp(r->method,"POST")||!strcmp(r->method,"GET")){+ ap_rputs("You used a GET or a POST method, that makes us happy!<br>",r);+}+else{+ ap_rputs("You did not use POST or GET, that makes us sad :(<br>",r);+}+ +/* Lastly, if there was a query string, let's print that too! */+if(r->args){+ ap_rprintf(r,"Your query string was:%s",r->args);+}+returnOK;+}+
+Apache relies on return values from handlers to signify whether a request was handled or not,
+and if so, whether the request went well or not. If a module is not interested in handling
+a specific request, it should always return the value DECLINED. If it is handling
+a request, it should either return the generic value OK, or a specific HTTP status
+code, for example:
+
+
+
+
++ + + +Returningstaticintexample_handler(request_rec*r)+{+/* Return 404: Not found */+returnHTTP_NOT_FOUND;+}+
OK or a HTTP status code does not necessarilly mean that the request will end. Apache
+may still have other handlers that are interested in this request, for instance the logging modules
+which, upon a successful request, will write down a summary of what was requested and how it went.
+To do a full stop and prevent any further processing after your module is done, you can return the
+value DONE to let Apache know that it should cease all activity on this request and
+carry on with the next, without informing other handlers.
+DECLINED: We are not handling this requestOK: We handled this request and it went wellDONE: We handled this request and Apache should just close this thread without further processingHTTP_OK (200): Request was okayHTTP_MOVED_PERMANENTLY (301): The resource has moved to a new URLHTTP_UNAUTHORIZED (401): Client is not authorized to visit this pageHTTP_FORBIDDEN (403): Permission deniedHTTP_NOT_FOUND (404): File not foundHTTP_INTERNAL_SERVER_ERROR (500): Internal server error (self explanatory)ap_rputs(const char *string, request_req *r): ap_rputs+ + + +("Hello, world!",r);
+ ap_rprintf: printf, except it sends the result to the client.
+
+
+
++ + +ap_rprintf(r,"Hello,%s!",r->useragent_ip);
+ ap_set_content_type(request_req *r, const char *type): + + +ap_set_content_type(r,"text/plain");/* force a raw text output */
+Managing your resources in Apache is quite easy, thanks to the memory pool system. In essence, +each server, connection and request have their own memory pool that gets cleaned up when its +scope ends, e.g. when a request is done or when a server process shuts down. All your module +needs to do is latch onto this memory pool, and you won't have to worry about having to clean +up after yourself - pretty neat, huh?
+ +
+In our module, we will primarilly be allocating memory for each request, so it's appropriate to
+use the reference when creating new objects. A few of the functions for
+allocating memory within a pool are:
+r->pool
void* apr_palloc(
+apr_pool_t *p, apr_size_t size): Allocates size number of bytes in the pool for youvoid* apr_pcalloc(
+apr_pool_t *p, apr_size_t size): Allocates size number of bytes in the pool for you and sets all bytes to 0char* apr_pstrdup(
+apr_pool_t *p, const char *s): Creates a duplicate of the string s. This is useful for copying constant values so you can edit them++ + +This is all well and good for our module, which won't need any pre-initialized variables or structures. +However, if we wanted to initialize something early on, before the requests come rolling in, we could +simply add a call to a function in ourstaticintexample_handler(request_rec*r)+{+constchar*original="You can't edit this!";+/* Allocate space for 10 integer values and set them all to zero. */+int*integers=apr_pcalloc(r->pool,sizeof(int)*10);+ +/* Create a copy of the 'original' variable that we can edit. */+char*copy=apr_pstrdup(r->pool,original);+returnOK;+}+
register_hooks function to sort it out:
+
+
+++ + +In this, pre-request initialization function, we would not be using the same pool as we did when +allocating resources for request-based functions. Instead, we would use the pool given to us by +Apache for allocating memory on a per-process based level. + + +staticvoidregister_hooks(apr_pool_t*pool)+{+/* Call a function that initializes some stuff */+example_init_function(pool);+/* Create a hook in the request handler, so we get called when a request arrives */+ap_hook_handler(example_handler,NULL,NULL,APR_HOOK_LAST);+}+
+In our example module, we would like to add a feature, that checks which type of digest, MD5 or SHA1
+the client would like to see. This could be solved by adding a query string to the request. A query
+string is typically comprised of several keys and values put together in a string, for instance
+valueA=yes&valueB=no&valueC=maybe. It is up to the module itself to parse these
+and get the data it requires. In our example, we'll be looking for a key called digest,
+and if set to md5, we'll produce an MD5 digest, otherwise we'll produce a SHA1 digest.
+
+Since the introduction of Apache 2.4, parsing request data from GET and POST requests have never +been easier. All we require to parse both GET and POST data is four simple lines: + + + +
+apr_table_t *GET;
+apr_array_header_t *POST;
+
+ap_args_to_table(r, &GET);
+ap_parse_form_data(r, NULL, &POST, -1, 8192);
+
+
+
+
+In our specific example module, we're looking for the digest value from the query string,
+which now resides inside a table called GET. To extract this value, we need only perform
+a simple operation:++ + + +The structures used for the POST and GET data are not exactly the same, so if we were to fetch a value from +POST data instead of the query string, we would have to resort to a few more lines, as outlined in +this example in the last chapter of this document. + +/* Get the "digest" key from the query string, if any. */+constchar*digestType=apr_table_get(GET,"digest");+ +/* If no key was returned, we will set a default value instead. */+if(!digestType)digestType="sha1";+ +
++ + + +This version in its entirity can be found here: mod_example_2.c. +staticintexample_handler(request_rec*r)+{+intrc,exists;+ apr_finfo_t finfo;+ apr_file_t*file;+char*filename;+charbuffer[256];+ apr_size_t readBytes;+intn;+ apr_table_t*GET;+ apr_array_header_t*POST;+constchar*digestType;+ + +/* Check that the "example-handler" handler is being called. */+if(!r->handler||strcmp(r->handler,"example-handler"))return(DECLINED);+ +/* Figure out which file is being requested by removing the .sum from it */+ filename=apr_pstrdup(r->pool,r->filename);+ filename[strlen(filename)-4]=0;/* Cut off the last 4 characters. */+ +/* Figure out if the file we request a sum on exists and isn't a directory */+ rc=apr_stat(&finfo,filename,APR_FINFO_MIN,r->pool);+if(rc==APR_SUCCESS){+ exists=+(+(finfo.filetype!=APR_NOFILE)+&&!(finfo.filetype&APR_DIR)+);+if(!exists)returnHTTP_NOT_FOUND;/* Return a 404 if not found. */+}+/* If apr_stat failed, we're probably not allowed to check this file. */+elsereturnHTTP_FORBIDDEN;+ +/* Parse the GET and, optionally, the POST data sent to us */+ + ap_args_to_table(r,&GET);+ ap_parse_form_data(r,NULL,&POST,-1,8192);+ +/* Set the appropriate content type */+ ap_set_content_type(r,"text/html");+ +/* Print a title and some general information */+ ap_rprintf(r,"<h2>Information on%s:</h2>",filename);+ ap_rprintf(r,"<b>Size:</b>%ubytes<br/>",finfo.size);+ +/* Get the digest type the client wants to see */+ digestType=apr_table_get(GET,"digest");+if(!digestType)digestType="MD5";+ + + rc=apr_file_open(&file,filename,APR_READ,APR_OS_DEFAULT,r->pool);+if(rc==APR_SUCCESS){+ +/* Are we trying to calculate the MD5 or the SHA1 digest? */+if(!strcasecmp(digestType,"md5")){+/* Calculate the MD5 sum of the file */+union{+charchr[16];+ uint32_t num[4];+}digest;+ apr_md5_ctx_t md5;+ apr_md5_init(&md5);+ readBytes=256;+while(apr_file_read(file,buffer,&readBytes)==APR_SUCCESS){+ apr_md5_update(&md5,buffer,readBytes);+}+ apr_md5_final(digest.chr,&md5);+ +/* Print out the MD5 digest */+ ap_rputs("<b>MD5: </b><code>",r);+for(n=0;n<APR_MD5_DIGESTSIZE/4;n++){+ ap_rprintf(r,"%08x",digest.num[n]);+}+ ap_rputs("</code>",r);+/* Print a link to the SHA1 version */+ ap_rputs("<br/><a href='?digest=sha1'>View the SHA1 hash instead</a>",r);+}+else{+/* Calculate the SHA1 sum of the file */+union{+charchr[20];+ uint32_t num[5];+}digest;+ apr_sha1_ctx_t sha1;+ apr_sha1_init(&sha1);+ readBytes=256;+while(apr_file_read(file,buffer,&readBytes)==APR_SUCCESS){+ apr_sha1_update(&sha1,buffer,readBytes);+}+ apr_sha1_final(digest.chr,&sha1);+ +/* Print out the SHA1 digest */+ ap_rputs("<b>SHA1: </b><code>",r);+for(n=0;n<APR_SHA1_DIGESTSIZE/4;n++){+ ap_rprintf(r,"%08x",digest.num[n]);+}+ ap_rputs("</code>",r);+ +/* Print a link to the MD5 version */+ ap_rputs("<br/><a href='?digest=md5'>View the MD5 hash instead</a>",r);+}+ apr_file_close(file);+ +}+ + + +/* Let Apache know that we responded to this request. */+returnOK;+}+
+In this next segment of this document, we will turn our eyes away from the digest module and create a new +example module, whose only function is to write out its own configuration. The purpose of this is to +examine how Apache works with configuration, and what happens when you start writing advanced configurations +for your modules. +
+mod_rewrite works:
+
+RewriteEngine On
+RewriteCond %{REQUEST_URI} ^/foo/bar
+RewriteRule ^/foo/bar/(.*)$ /foobar?page=$1
+++ + + +Now, let's put this into perspective by creating a very small module that just prints out a hard-coded +configuration. You'll notice that we use thetypedefstruct{+intenabled;/* Enable or disable our module */+constchar*path;/* Some path to...something */+inttypeOfAction;/* 1 means action A, 2 means action B and so on */+}example_config;+
register_hooks function for initializing the
+configuration values to their defaults:
+
+
+
+++ + + +So far so good. To access our new handler, we could add the following to our configuration: +typedefstruct{+intenabled;/* Enable or disable our module */+constchar*path;/* Some path to...something */+inttypeOfAction;/* 1 means action A, 2 means action B and so on */+}example_config;+ +staticexample_config config;+ +staticintexample_handler(request_rec*r)+{+if(!r->handler||strcmp(r->handler,"example-handler"))return(DECLINED);+ ap_set_content_type(r,"text/plain");+ ap_rprintf(r,"Enabled:%u\n",config.enabled);+ ap_rprintf(r,"Path:%s\n",config.path);+ ap_rprintf(r,"TypeOfAction:%x\n",config.typeOfAction);+returnOK;+}+ +staticvoidregister_hooks(apr_pool_t*pool)+{+ config.enabled=1;+ config.path="/foo/bar";+ config.typeOfAction=0x00;+ ap_hook_handler(example_handler,NULL,NULL,APR_HOOK_LAST);+}+ +/* Define our module as an entity and assign a function for registering hooks */+ +module AP_MODULE_DECLARE_DATA example_module=+{+ STANDARD20_MODULE_STUFF,+NULL,/* Per-directory configuration handler */+NULL,/* Merge handler for per-directory configurations */+NULL,/* Per-server configuration handler */+NULL,/* Merge handler for per-server configurations */+NULL,/* Any directives we may have for httpd */+ register_hooks/* Our hook registering function */+};+
+<Location /example> + SetHandler example-handler +</Location> +
+module AP_MODULE_DECLARE_DATA example_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL, /* Per-directory configuration handler */
+ NULL, /* Merge handler for per-directory configurations */
+ NULL, /* Per-server configuration handler */
+ NULL, /* Merge handler for per-server configurations */
+ example_directives, /* Any directives we may have for httpd */
+ register_hooks /* Our hook registering function */
+};
+
+
+
+
+This will tell Apache that we are now accepting directives from the configuration files, and that the
+structure called example_directives holds information on what our directives are and how
+they work. Since we have three different variables in our module configuration, we will add a structure
+with three directives and a NULL at the end:
+
+
+
+
+static const command_rec example_directives[] =
+{
+ AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, RSRC_CONF, "Enable or disable mod_example"),
+ AP_INIT_TAKE1("examplePath", example_set_path, NULL, RSRC_CONF, "The path to whatever"),
+ AP_INIT_TAKE2("exampleAction", example_set_action, NULL, RSRC_CONF, "Special action value!"),
+ { NULL }
+};
+
+
+
+
+
+As you can see, each directive needs at least 5 parameters set: +
AP_INIT_TAKE1: This is a macro that tells Apache that this directive takes one and only one argument.
+If we required two arguments, we could use the macro AP_INIT_TAKE2 and so on (refer to httpd_conf.h
+for more macros).exampleEnabled: This is the name of our directive. More precisely, it is what the user must put in his/her
+configuration in order to invoke a configuration change in our module.example_set_enabled: This is a reference to a C function that parses the directive and sets the configuration
+accordingly. We will discuss how to make this in the following paragraph.RSRC_CONF: This tells Apache where the directive is permissable. We'll go into details on this value in the
+later chapters, but for now, RSRC_CONF means that Apache will only accept these directives in a server context."Enable or disable....": This is simply a brief description of what the directive does.NULL, is an optional function that can be
+run after the initial function to parse the arguments have been run. This is usually omitted, as the function for verifying
+arguments might as well be used to set them.)
+
+
+Now that we've told Apache to expect some directives for our module, it's time to make a few functions for handling these. What
+Apache reads in the configuration file(s) is text, and so naturally, what it passes along to our directive handler is one or
+more strings, that we ourselves need to recognize and act upon. You'll notice, that since we set our exampleAction
+directive to accept two arguments, its C function also has an additional parameter defined:
+
+
+
+
++ + + + +/* Handler for the "exambleEnabled" directive */+constchar*example_set_enabled(cmd_parms*cmd,void*cfg,constchar*arg)+{+if(!strcasecmp(arg,"on"))config.enabled=1;+elseconfig.enabled=0;+returnNULL;+}+ +/* Handler for the "examplePath" directive */+constchar*example_set_path(cmd_parms*cmd,void*cfg,constchar*arg)+{+ config.path=arg;+returnNULL;+}+ +/* Handler for the "exampleAction" directive */+/* Let's pretend this one takes one argument (file or db), and a second (deny or allow), */+/* and we store it in a bit-wise manner. */+constchar*example_set_action(cmd_parms*cmd,void*cfg,constchar*arg1,constchar*arg2)+{+if(!strcasecmp(arg1,"file"))config.typeOfAction=0x01;+elseconfig.typeOfAction=0x02;+ +if(!strcasecmp(arg2,"deny"))config.typeOfAction+=0x10;+elseconfig.typeOfAction+=0x20;+returnNULL;+}+
+Now that we have our directives set up, and handlers configured for them, we can assemble our module +into one big file: + + + +
++ + + + +/* mod_example_config_simple.c: */+#include<stdio.h>+#include"apr_hash.h"+#include"ap_config.h"+#include"ap_provider.h"+#include"httpd.h"+#include"http_core.h"+#include"http_config.h"+#include"http_log.h"+#include"http_protocol.h"+#include"http_request.h"+ +/*+==============================================================================+Our configuration prototype and declaration:+==============================================================================+*/+typedefstruct{+intenabled;/* Enable or disable our module */+constchar*path;/* Some path to...something */+inttypeOfAction;/* 1 means action A, 2 means action B and so on */+}example_config;+ +staticexample_config config;+ +/*+==============================================================================+Our directive handlers:+==============================================================================+*/+/* Handler for the "exambleEnabled" directive */+constchar*example_set_enabled(cmd_parms*cmd,void*cfg,constchar*arg)+{+if(!strcasecmp(arg,"on"))config.enabled=1;+elseconfig.enabled=0;+returnNULL;+}+ +/* Handler for the "examplePath" directive */+constchar*example_set_path(cmd_parms*cmd,void*cfg,constchar*arg)+{+ config.path=arg;+returnNULL;+}+ +/* Handler for the "exampleAction" directive */+/* Let's pretend this one takes one argument (file or db), and a second (deny or allow), */+/* and we store it in a bit-wise manner. */+constchar*example_set_action(cmd_parms*cmd,void*cfg,constchar*arg1,constchar*arg2)+{+if(!strcasecmp(arg1,"file"))config.typeOfAction=0x01;+elseconfig.typeOfAction=0x02;+ +if(!strcasecmp(arg2,"deny"))config.typeOfAction+=0x10;+elseconfig.typeOfAction+=0x20;+returnNULL;+}+ +/*+==============================================================================+The directive structure for our name tag:+==============================================================================+*/+staticconstcommand_rec example_directives[]=+{+ AP_INIT_TAKE1("exampleEnabled",example_set_enabled,NULL,RSRC_CONF,"Enable or disable mod_example"),+ AP_INIT_TAKE1("examplePath",example_set_path,NULL,RSRC_CONF,"The path to whatever"),+ AP_INIT_TAKE2("exampleAction",example_set_action,NULL,RSRC_CONF,"Special action value!"),+{NULL}+};+/*+==============================================================================+Our module handler:+==============================================================================+*/+staticintexample_handler(request_rec*r)+{+if(!r->handler||strcmp(r->handler,"example-handler"))return(DECLINED);+ ap_set_content_type(r,"text/plain");+ ap_rprintf(r,"Enabled:%u\n",config.enabled);+ ap_rprintf(r,"Path:%s\n",config.path);+ ap_rprintf(r,"TypeOfAction:%x\n",config.typeOfAction);+returnOK;+}+ +/*+==============================================================================+The hook registration function (also initializes the default config values):+==============================================================================+*/+staticvoidregister_hooks(apr_pool_t*pool)+{+ config.enabled=1;+ config.path="/foo/bar";+ config.typeOfAction=3;+ ap_hook_handler(example_handler,NULL,NULL,APR_HOOK_LAST);+}+/*+==============================================================================+Our module name tag:+==============================================================================+*/+module AP_MODULE_DECLARE_DATA example_module=+{+ STANDARD20_MODULE_STUFF,+NULL,/* Per-directory configuration handler */+NULL,/* Merge handler for per-directory configurations */+NULL,/* Per-server configuration handler */+NULL,/* Merge handler for per-server configurations */+ example_directives,/* Any directives we may have for httpd */+ register_hooks/* Our hook registering function */+};+
+In our httpd.conf file, we can now change the hard-coded configuration by adding a few lines:
+
+ExampleEnabled On
+ExamplePath "/usr/bin/foo"
+ExampleAction file allow
+
/example on our web site, and we see the configuration has
+adapted to what we wrote in our configuration file.
+
+In Apache, different URLs, virtual hosts, directories etc can have very different meanings
+to the user of Apache, and thus different contexts within which modules must operate. For example,
+let's assume you have this configuration set up for mod_rewrite:
+
+<Directory "/var/www">
+ RewriteCond %{HTTP_HOST} ^example.com$
+ RewriteRule (.*) http://www.example.com/$1
+</Directory>
+<Directory "/var/www/sub">
+ RewriteRule ^foobar$ index.php?foobar=true
+</Directory>
+
/var/www, all requests for http://example.com must go to http://www.example.com/var/www/sub, all requests for foobar must go to index.php?foobar=true+So how does a module get the specific configuration for the server, directory or location in question? It does so by making one simple call: + + + +
+
+
+
+example_config *config = (example_config*) ap_get_module_config(r->per_dir_config, &example_module);
+
+That's it! Of course, a whole lot goes on behind the scenes, which we will discuss in this chapter, starting with how
+Apache came to know what our configuration looks like, and how it came to be set up as it is in the specific context.
+
+In this chapter, we will be working with a slightly modified version of our previous
+context structure. We will set a context variable that we can use to track
+which context configuration is being used by Apache in various places:
+
+
+
+
++ + + + + +typedefstruct{+charcontext[256];+charpath[256];+inttypeOfAction;+intenabled;+}example_config;+
Our handler for requests will also be modified, yet still very simple: + + + +
++ + + + + +staticintexample_handler(request_rec*r)+{+if(!r->handler||strcmp(r->handler,"example-handler"))return(DECLINED);+ example_config*config=(example_config*)ap_get_module_config(r->per_dir_config,&example_module);+ ap_set_content_type(r,"text/plain");+ ap_rprintf("Enabled:%u\n",config->enabled);+ ap_rprintf("Path:%s\n",config->path);+ ap_rprintf("TypeOfAction:%x\n",config->typeOfAction);+ ap_rprintf("Context:%s\n",config->context);+returnOK;+}+
+Before we can start making our module context aware, we must first define, which contexts we will accept. +As we saw in the previous chapter, defining a directive required five elements be set: + + + +
++ + + +TheAP_INIT_TAKE1("exampleEnabled", example_set_enabled,NULL, RSRC_CONF, "Enable or disable mod_example"), +
RSRC_CONF definition told Apache that we would only allow this directive in a global server context, but
+since we are now trying out a context aware version of our module, we should set this to something more lenient, namely
+the value ACCESS_CONF, which lets us use the directive inside <Directory> and <Location> blocks.
+
+A much smarter way to manage your configurations is by letting Apache help you create them. +To do so, we must first start off by chancing our name tag to let Apache know, that +it should assist us in creating and managing our configurations. Since we have chosen the per-directory +(or per-location) context for our module configurations, we'll add a per-directory creator and merger +function reference in our tag: + + +
+module AP_MODULE_DECLARE_DATA example_module+ + + + + + + +=+{+ STANDARD20_MODULE_STUFF,+ create_dir_conf,/* Per-directory configuration handler */+ merge_dir_conf,/* Merge handler for per-directory configurations */+NULL,/* Per-server configuration handler */+NULL,/* Merge handler for per-server configurations */+ directives,/* Any directives we may have for httpd */+ register_hooks/* Our hook registering function */+};+
+Now that we have told Apache to help us create and manage configurations, our first step is to +make a function for creating new, blank configurations. We do so by creating the function we just +referenced in our name tag as the Per-directory configuration handler: + +
++ + + + +void*example_create_dir_conf(apr_pool_t*pool,char*context){+ context=context?context:"(undefined context)";+ example_config*cfg=apr_pcalloc(pool,sizeof(example_config));+if(cfg){+/* Set some default values */+strcpy(cfg->context,x);+ cfg->enabled=0;+ cfg->path="/foo/bar";+ cfg->typeOfAction=0x11;+}+returndir;+}+
+Our next step in creating a context aware configuration is merging configurations. This part of the process
+particularly apply to scenarios where you have a parent configuration and a child, such as the following:
+
+<Directory "/var/www">
+ ExampleEnable On
+ ExamplePath /foo/bar
+ ExampleAction file allow
+</Directory>
+<Directory "/var/www/subdir">
+ ExampleAction file deny
+</Directory>
+
/var/www/subdir should inherit the
+value set for the /var/www directory, as we did not specify a ExampleEnable nor an
+ExamplePath for this directory. Apache does not presume to know if this is true, but cleverly
+does the following:
+
/var/www/var/www/var/www/subdir/var/www/subdir/var/www/subdirmerge_dir_conf function we referenced in our name tag. The purpose of
+this function is to assess the two configurations and decide how they are to be merged:
+
+
+
+++ + + +void*merge_dir_conf(apr_pool_t*pool,void*BASE,void*ADD){+ example_config*base=BASE;+ example_config*add=ADD;+ example_config*conf=create_dir_conf(pool,"Merged configuration");+ + conf->enabled=(add->enabled==0)?base->enabled:add->enabled;+ conf->typeOfAction=add->typeOfAction?add->typeOfAction:base->typeOfAction;+strcpy(conf->path,strlen(add->path)?add->path:base->path);+ +returnconf;+}+
+Now, let's try putting it all together to create a new module that it context aware. First off, we'll
+create a configuration that lets us test how the module works:
+
+<Location "/a">
+ SetHandler example-handler
+ ExampleEnabled on
+ ExamplePath "/foo/bar"
+ ExampleAction file allow
+</Location>
+
+<Location "/a/b">
+ ExampleAction file deny
+ ExampleEnabled off
+</Location>
+
+<Location "/a/b/c">
+ ExampleAction db deny
+ ExamplePath "/foo/bar/baz"
+ ExampleEnabled on
+</Location>
+
++ + + +/*$6+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++* mod_example_config.c+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/+ + +#include<stdio.h>+#include"apr_hash.h"+#include"ap_config.h"+#include"ap_provider.h"+#include"httpd.h"+#include"http_core.h"+#include"http_config.h"+#include"http_log.h"+#include"http_protocol.h"+#include"http_request.h"+ +/*$1+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Configuration structure+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+*/+ +typedefstruct+{+charcontext[256];+charpath[256];+inttypeOfAction;+intenabled;+}example_config;+ +/*$1+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Prototypes+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+*/+ +staticintexample_handler(request_rec*r);+constchar*example_set_enabled(cmd_parms*cmd,void*cfg,constchar*arg);+constchar*example_set_path(cmd_parms*cmd,void*cfg,constchar*arg);+constchar*example_set_action(cmd_parms*cmd,void*cfg,constchar*arg1,constchar*arg2);+void*create_dir_conf(apr_pool_t*pool,char*context);+void*merge_dir_conf(apr_pool_t*pool,void*BASE,void*ADD);+staticvoidregister_hooks(apr_pool_t*pool);+ +/*$1+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Configuration directives+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+*/+ +staticconstcommand_rec directives[]=+{+ AP_INIT_TAKE1("exampleEnabled",example_set_enabled,NULL,ACCESS_CONF,"Enable or disable mod_example"),+ AP_INIT_TAKE1("examplePath",example_set_path,NULL,ACCESS_CONF,"The path to whatever"),+ AP_INIT_TAKE2("exampleAction",example_set_action,NULL,ACCESS_CONF,"Special action value!"),+{NULL}+};+ +/*$1+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Our name tag+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+*/+ +module AP_MODULE_DECLARE_DATA example_module=+{+ STANDARD20_MODULE_STUFF,+ create_dir_conf,/* Per-directory configuration handler */+ merge_dir_conf,/* Merge handler for per-directory configurations */+NULL,/* Per-server configuration handler */+NULL,/* Merge handler for per-server configurations */+ directives,/* Any directives we may have for httpd */+ register_hooks/* Our hook registering function */+};+ +/*+=======================================================================================================================+Hook registration function+=======================================================================================================================+*/+staticvoidregister_hooks(apr_pool_t*pool)+{+ ap_hook_handler(example_handler,NULL,NULL,APR_HOOK_LAST);+}+ +/*+=======================================================================================================================+Our example web service handler+=======================================================================================================================+*/+staticintexample_handler(request_rec*r)+{+if(!r->handler||strcmp(r->handler,"example-handler"))return(DECLINED);+ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ example_config*config=(example_config*)ap_get_module_config(r->per_dir_config,&example_module);+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ + ap_set_content_type(r,"text/plain");+ ap_rprintf(r,"Enabled:%u\n",config->enabled);+ ap_rprintf(r,"Path:%s\n",config->path);+ ap_rprintf(r,"TypeOfAction:%x\n",config->typeOfAction);+ ap_rprintf(r,"Context:%s\n",config->context);+returnOK;+}+ +/*+=======================================================================================================================+Handler for the "exambleEnabled" directive+=======================================================================================================================+*/+constchar*example_set_enabled(cmd_parms*cmd,void*cfg,constchar*arg)+{+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ example_config*conf=(example_config*)cfg;+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ +if(conf)+{+if(!strcasecmp(arg,"on"))+ conf->enabled=1;+else+ conf->enabled=0;+}+ +returnNULL;+}+ +/*+=======================================================================================================================+Handler for the "examplePath" directive+=======================================================================================================================+*/+constchar*example_set_path(cmd_parms*cmd,void*cfg,constchar*arg)+{+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ example_config*conf=(example_config*)cfg;+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ +if(conf)+{+strcpy(conf->path,arg);+}+ +returnNULL;+}+ +/*+=======================================================================================================================+Handler for the "exampleAction" directive ;+Let's pretend this one takes one argument (file or db), and a second (deny or allow), ;+and we store it in a bit-wise manner.+=======================================================================================================================+*/+constchar*example_set_action(cmd_parms*cmd,void*cfg,constchar*arg1,constchar*arg2)+{+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ example_config*conf=(example_config*)cfg;+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ +if(conf)+{+{+if(!strcasecmp(arg1,"file"))+ conf->typeOfAction=0x01;+else+ conf->typeOfAction=0x02;+if(!strcasecmp(arg2,"deny"))+ conf->typeOfAction+=0x10;+else+ conf->typeOfAction+=0x20;+}+}+ +returnNULL;+}+ +/*+=======================================================================================================================+Function for creating new configurations for per-directory contexts+=======================================================================================================================+*/+void*create_dir_conf(apr_pool_t*pool,char*context)+{+ context=context?context:"Newly created configuration";+ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ example_config*cfg=apr_pcalloc(pool,sizeof(example_config));+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ +if(cfg)+{+{+/* Set some default values */+strcpy(cfg->context,context);+ cfg->enabled=0;+memset(cfg->path,0,256);+ cfg->typeOfAction=0x00;+}+}+ +returncfg;+}+ +/*+=======================================================================================================================+Merging function for configurations+=======================================================================================================================+*/+void*merge_dir_conf(apr_pool_t*pool,void*BASE,void*ADD)+{+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ example_config*base=BASE;+ example_config*add=ADD;+ example_config*conf=create_dir_conf(pool,"Merged configuration");+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ + conf->enabled=(add->enabled==0)?base->enabled:add->enabled;+ conf->typeOfAction=add->typeOfAction?add->typeOfAction:base->typeOfAction;+strcpy(conf->path,strlen(add->path)?add->path:base->path);+returnconf;+}+
+We have now looked at how to create simple modules for Apache and configuring them. What you do next is entirely up +to you, but it is my hope that something valuable has come out of reading this documentation. If you have questions +on how to further develop modules, you are welcome to join our mailing lists +or check out the rest of our documentation for further tips. +
+++ + + +constchar*read_post_value(constchar*key)+{+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+constapr_array_header_t*fields;+inti;+ apr_table_entry_t*e=0;+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ e=(apr_table_entry_t*)fields->elts;+for(i=0;i<fields->nelts;i++){+if(!strcmp(e[i].key,key))returne[i].val;+}+return0;+}+staticintexample_handler(request_req*r)+{+/*~~~~~~~~~~~~~~~~~~~~~~*/+ apr_array_header_t*POST;+constchar*value;+/*~~~~~~~~~~~~~~~~~~~~~~*/+ ap_parse_form_data(r,NULL,&POST,-1,8192);+ + value=read_post_value(POST,"valueA");+if(!value)value="(undefined)";+ ap_rprintf(r,"The value of valueA is:%s",value);+returnOK;+}+
++ + + +staticintexample_handler(request_req*r)+{+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+constapr_array_header_t*fields;+inti;+ apr_table_entry_t*e=0;+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ + fields=apr_table_elts(r->headers_in);+ e=(apr_table_entry_t*)fields->elts;+for(i=0;i<fields->nelts;i++){+ ap_rprintf(r,"<b>%s</b>:%s<br/>",e[i].key,e[i].val);+}+returnOK;+}+
++ + + +staticintutil_read(request_rec*r,constchar**rbuf,apr_off_t*size)+{+/*~~~~~~~~*/+intrc=OK;+/*~~~~~~~~*/+ +if((rc=ap_setup_client_block(r,REQUEST_CHUNKED_ERROR))){+return(rc);+}+ +if(ap_should_client_block(r)){+ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+charargsbuffer[HUGE_STRING_LEN];+ apr_off_t rsize,len_read,rpos=0;+ apr_off_t length=r->remaining;+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/+ +*rbuf=(constchar*)apr_pcalloc(r->pool,(apr_size_t)(length+1));+*size=length;+while((len_read=ap_get_client_block(r,argsbuffer,sizeof(argsbuffer)))>0){+if((rpos+len_read)>length){+ rsize=length-rpos;+}+else{+ rsize=len_read;+}+ +memcpy((char*)*rbuf+rpos,argsbuffer,(size_t)rsize);+ rpos+=rsize;+}+}+return(rc);+}+ +staticintexample_handler(request_req*r)+{+/*~~~~~~~~~~~~~~~~*/+ apr_off_t size;+constchar*buffer;+/*~~~~~~~~~~~~~~~~*/+ +if(util_read(r,&data,&size)==OK){+ ap_rprintf("We read a request body that was%ubytes long",size);+}+returnOK;+}+