From e4a5590c05ad0d43db171d375f5a7d64336071ed 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 code
See 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/subdir
merge_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, ++ + + +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 */
+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:
+
+
+
+
++ + + +Thestatic
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.
+
++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". +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 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:
+
+
+
+
++ + + + +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;
+}
+
+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:
+
+
+
+
++ + + +Returningstatic
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.
+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 ourstatic
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)
;
+}
+
+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. */
+const
char
*
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. +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;
+}
+
+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 thetypedef
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> +
+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 calledNULL
,/* 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
} +}; +
+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 */
+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
;
+}
+
+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:
+==============================================================================
+*/
+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 */
+}
;
+
+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:
+
+
+
+
++ + + + + +typedef
struct
{
+char
context[
256
]
;
+char
path[
256
]
;
+int
typeOfAction;
+int
enabled;
+}
example_config;
+
Our handler for requests will also be modified, yet still very simple: + + + +
++ + + + + +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;
+}
+
+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
;
+}
+return
dir;
+}
+
+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/subdir
merge_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;
+}
+
+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
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+ +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;
+}
+
+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. +
+++ + + +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;
+}
+
++ + + +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;
+}
+
++ + + +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;
+}
+