From 7bfaca6c0235bad2ae1284a45581c6cb01e992e3 Mon Sep 17 00:00:00 2001
From: Daniel Gruno 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:
+
+
+
+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, if 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 http://ci.apache.org/projects/httpd/trunk/doxygen/structrequest__rec.html.
+
+
+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 r->pool
+reference when creating new objects. A few of the functions for allocating
+memory within a pool are:
+
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 themchar* apr_psprintf(
+apr_pool_t *p, const char *fmt, ...)
: Similar to sprintf
, except Apache supplies you with an appropriately allocated target variable++ + +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!
"
;
+char
*
copy;
+int
*
integers;
+ +/* Allocate space for 10 integer values and set them all to zero. */
+ integers=
apr_pcalloc(
r-
>
pool,
sizeof
(
int
)
*
10
)
;
+ +/* Create a copy of the 'original' variable that we can edit. */
+ 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 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> +
+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 changing 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
cfg;
+}
+
+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=
(
example_config*
)
BASE;
+ example_config*
add=
(
example_config*
)
ADD;
+ example_config*
conf=
(
example_config*
)
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 is
+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=
(
example_config*
)
BASE;
+ example_config*
add=
(
example_config*
)
ADD;
+ example_config*
conf=
(
example_config*
)
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
apr_array_header_t*
fields,
const
char
*
key)
+{
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+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;
+}
+