Apache 2.0 provides many API improvements. This article provides an example of the Apache 2.0 filter module and uses the example to describe the new API.
Apache becomes the most popular Web server, in part because it can obtain a large number of Server Extensions developed by a third party, and because of its open architecture, it is very easy to develop its own extensions. Of course, nothing is absolutely easy. Therefore, during the development of Apache 2.0, a major goal is to improve Apache API to make development and expansion easier.
A key change is the specialization of a very general choice of a typical extension module, which makes it very easy to develop such a specialized subset. Apache 2.0 has dedicated APIs for development modules. These modules only need to modify the content of user responses or the details of users' HTTP requests. These APIs are called output filters and input filters respectively. The output filter is the most common. A good example is the standard Apache 2.0 module, which is used to calculate the length of the content returned to the user so as to update the appropriate header and log items. Another example is the module used for automatic spelling check of outbound content (for example, the module in Apache 1.3 cleverly named "mod_speling ).
Install
Apache 2.0 is still not perfect, and its documentation is one aspect that developers cannot fully understand (I believe this is a good thing for code and document writers ). I will outline the steps taken to set up Apache 2.0 installation suitable for the development module. I have obtained httpd-2.0.39.tar.gz from the Apache FTP site (see references for links) and decompressed it to an appropriate directory. Next, I will use three combinations of commands that are very familiar to any recent Unix users to build code:
./Configure -- prefix =/usr/local/Apache/
Make
Make install if you do not want to use the default value "/usr/local/", use the -- prefix option of configure. Now, you need to build a complete API documentation because it seems that they cannot be obtained online, and they are required by the Apache module developers. To build these documents, you need scandoc (see references), which generates HTML documents from special comments in the code like javadoc. I downloaded scandoc 0.14 and decompressed it to the same directory as the Apache source code. Then, from the Created directory:
$ CP scandoc ../httpd-2.0.39/srclib/APR/build/scandoc. pl
$ CP-r images/../httpd-2.0.39/docs/I must also apply patches to Apache source code to avoid affecting scandoc. This small patch is shown in Listing 1.
List 1. patches that allow document generation
--- Include/util_time.h.old 00:59:28. 000000000-0600
++ Include/util_time.h 00:59:37. 000000000-0600
@-+ @@
# Endif
/**
-* @ Package Apache date/time handling functions
+ * @ Package Apache date-time handling functions
*/
/* Maximum delta from the current time, in seconds, for a past time due to this work, therefore, you get a partial return-the API documentation in the docs/API subdirectory under the source directory. Let's start with docs/API/index.html. The make install command does not seem to have done anything to the API documentation. Therefore, you may need to manually create a soft link between the API directory and the "Manual" directory created by make install.
I said "partial return" because the document comment in the Apache header file seems to have some obvious defects. The generated document shows many functions without names. I no longer use the generated documents as a good starting point to find the information I need, and then find the source code when necessary. I did find that the convenient technology for searching for API functions is to search for the include directory using a command similar to the following:
Grep-C7 AP. * _ declare/usr/local/include/* | grep-C7 [search-Keyword] simple output filter
The output filter modifies the content or header generated by other modules in some way. The simple example I will demonstrate is a filter, which looks for the magic string "*** time-Cookie ***" and replaces it with the current local time display on the server. Of course, you can use Apache server-side include or other utilities like this to easily complete this task, but this example allows us to demonstrate Apache API. We do not always use the current time: if the magic string appears multiple times in the content, the filter will use the same time stamp each time, even if there is a time interval between the time when such strings are found for the first time and the time when the strings are subsequently found. This method demonstrates the management of the filter context.
The Apache 2.0 runtime model is well designed and operates as if the content liquid flows from one filter to another. In fact, the Apache team chooses a metaphor to compare it to a long queue to send a bucket for fire. A filter fills the bucket with content and passes it to the next filter in the chain. In this way, a filter may be called multiple times during the processing of an HTTP transaction, just as different blocks pass through the "Bucket queue ". For all the most common filters, this means that the filter must be able to save a certain context between two calls. As described in my example, the filter must remember the timestamp when replacing the first string. Listing 2 shows the filter.
# Include "httpd. H"
# Include "util_filter.h"
# Include "http_config.h"
# Include "http_log.h"
/* The string which is to be replaced by the time stamp */
Static char time_cookie [] = "*** time-Cookie ***";
/* Declare the module name */
Module ap_module_declare_data time_cookie;
Typedef struct tc_context _{
Apr_bucket_brigade * BB;
Apr_time_t timestamp;
} Tc_context;
/*
This function passes in the system filter information (f)
And the bucket brigade representing content to be filtered (bb)
*/
Static int time_cookie_filter (ap_filter_t * F, apr_bucket_brigade * bb)
{
Tc_context * CTX = f-> CTX;/* the filter context */
Apr_bucket * curr_bucket;
Apr_pool_t * Pool = f-> r-> pool;/* the pool for all memory requests */
/* The buffer where we shall place the time stamp string.
Apr_rfc822_date_len the fixed length of such strings */
Char time_str [apr_rfc822_date_len + 1];
Apr_time_t timestamp;
If (CTX = NULL ){
/* The first time this filter has been invoked for this transaction */
F-> CTX = apr_pcalloc (F-> r-> pool, sizeof (* CTX ));
CTX-> BB = apr_brigade_create (F-> r-> pool, F-> C-> bucket_alloc );
Timestamp = apr_time_now ();
CTX-> timestamp = timestamp;
}
Else {
/* Get the time stamp we 've already set */
Timestamp = CTX-> timestamp;
}
/* Render the time into a string in rfc822 format */
Apr_rfc822_date (time_str, timestamp );
/*
Iterate over each bucket in the brigade.
Find each "cookie" in the "Kitchen" and replace with the time stamp
*/
Apr_brigade_foreach (curr_bucket, BB ){
Const char * kitchen, * cookie;
Apr_size_t Len;
If (apr_bucket_is_eos (curr_bucket) | apr_bucket_is_flush (curr_bucket )){
Apr_bucket_remove (curr_bucket );
Apr_brigade_insert_tail (CTX-> BB, curr_bucket );
Ap_pass_brigade (F-> next, CTX-> BB );
Return apr_success;
}
Apr_bucket_read (curr_bucket, & kitchen, & Len, apr_nonblock_read );
While (kitchen & strcmp (kitchen ,"")){
/* Return a poiner to the next occurrence of the cookie */
Cookie = ap_strstr (kitchen, time_cookie );
If (cookie ){
/* Write the text up to the cookie, then the cookie
To the next filter in the chain
*/
Ap_fwrite (F-> next, CTX-> BB, kitchen, cookie-kitchen );
Ap_fputs (F-> next, CTX-> BB, time_str );
Kitchen = cookie + sizeof (time_cookie)-1;
/*
The following is an example of writing to the error log.
The message is actually not really appropriate for the error log,
But it serves as example.
*/
Ap_log_rerror (aplog_mark, aplog_err, 0, F-> r,
"Replacing cookie with/" % S/"", time_str );
} Else {
/* No more cookies found, so just write the rest of
String and flag that we're re done
*/
Ap_fputs (F-> next, CTX-> BB, kitchen );
Kitchen = "";
}
}
}
Return apr_success;
}
/* Register the filter function as a filter for modifying the HTTP body (content )*/
Static void time_cookie_register_hook (apr_pool_t * Pool)
{
Ap_register_output_filter ("timecookie", time_cookie_filter, null,
Ap_ftype_content_set );
}
/* Define the module data */
Module ap_module_declare_data time_cookie =
{
Standard20_module_stuff,
Null,/* dir config creater */
Null,/* dir merger --- default is to override */
Null,/* server config */
Null,/* merge server config */
Null,/* command apr_table_t */
Time_cookie_register_hook/* Register hook */
}; Determine which # include files are required. When the 2.0 API documentation is complete, it is easier to determine. The module name declaration name is very important. You must explicitly mount your module in the configuration file. (We will add the time Cookie module to my module below to show how it works .) They will specify the compiled target file and module name. Your module will be loaded from the target file, and the module name must match the ap_module_declare_data declaration. The next global structure, that is, the context structure, is also important. Because Filters may be called several times to complete the operation, most instances need to maintain information between each call. Because the same function may be called when processing multiple concurrent requests, you should not select global or static local variables because they are usually not present in multi-threaded programs. Apache API solves this problem by maintaining the module context between calls.
In the context, we track:
· Bucket queue, which is passed to the next filter in the queue. By following the analogy, we are collecting brand new buckets, just like the buckets filled with content that our previous filter gave us, we will modify the content as needed and put the content in the new bucket. The new bucket will be handed over to the next filter. We tracked these new buckets in the context.
· Time stamp, which is generated during the first call. It ensures that all cookie instances in the document are replaced with the same string no matter how long the request has taken.
In the filter function, context is passed in as part of the filter information structure, as is the incoming bucket queue. We will use this bucket queue as the source of content for processing. When a function is called for a request, the context at the beginning is actually null. Apache provides very well-developed functions, so that programmers no longer need to be troubled by most memory allocation problems. We choose to use the pool provided to us for resource requests, rather than creating our own pool, which may only be required in some very advanced situations. Apache also provides a time function library. Type apr_time_t is the number of units, which indicates a time point. It can be converted into any of several time notation, including human-readable string notation.
When the filter is called for the first time, the context structure is allocated and the output bucket queue is created. The current time stamp is also obtained. In all circumstances, this time is converted into a readable representation. The next step is to use apr_brigade_foreach to process the content. apr_brigade_foreach is a macro provided by Apache for iteration on the bucket queue. The first operation of the business is to find the special mark bucket used for the end of the queue. The filter always says, "I have already handled enough content: I want to pass it down and wait for me again ". This is done using the ap_fflush function. When the previous filter calls this function, we get a special bucket marked by apr_bucket_is_flush. Once it reaches the actual end of the bucket or content, we will clear the bucket queue, pass it to the next filter in the queue, and return apr_success, which makes Apache know there is no problem.
Finally, the most important part of the article. We read content from each bucket (it becomes the kitchen variable) and look for a cookie instance, copy all other text to the output queue as it is, and write a timestamp wherever the cookie is found (note: through "cookie" I actually refer to cookies as magic values in the conventional programming sense-instead of Web browser cookies ). I will naturally use the stream read/write function of Apache to operate the bucket, and I also use the strstr function of Apache. This function seems unnecessary at the beginning, because all relevant parameters are simple character pointers. Apache provides a complete string library. for resource management and security reasons, you should always use apache version.
Like many other programming frameworks, Apache APIs use hooks to call custom code. Hook is a function registered on Apache and called at a specific point. Some hooks are called during configuration when Apache is started. In this example, no such hooks are used, so they are expressed as null values. There are also some hooks for code called during the request. This type of hooks is used in this example. The hook registration function is called at startup. It specifies to register some other hooks to Apache. In this example, the filter registers itself in the normal filter chain. I set the filter to ap_ftype_content_set, indicating that it operates on the content. There are also some filter types used for operations on headers and network parameters. Next, fill in the module declaration declared at the top of the file. I only need to register the hook for the registration module. More complex Filters may need to register other hooks in the structure.
Try
It is very easy to try to complete this module. Build this module as follows:
Gcc-FPIC-I $ include-C time_cookie.c-O time_cookie.o
Gcc-shared-L $ Lib-lapr-laprutil time_cookie.o-O time_cookie.so ensure that $ include and $ lib contain the path to the Apache header file and library file. Then copy the generated time_cookie.so file to your Apache module File (for example,/usr/local/Apache/modules ). Next, add the following lines to your httpd. conf:
Loadmodule time_cookie modules/time_cookie.so
Addoutputfilter timecookie. html then use apachectl start or apachectl restart to start or restart the server. Next, copy listing 3 to the file time-cookie-test.html and place the file to a place that accepts the apache service. The easiest way is to copy it to the htdocs directory installed in Apache, if copied to this directory, you can see the result by browsing http: // localhost/time-cookie-test.html. Since not everyone has superuser access, I am working as a common user-including Apache installation itself-to ensure that it applies to anyone. Because Apache is installed in this way, it does not allow listening on port 80, so I am in httpd. set the listening port to 8000 in conf and use the URL http: // localhost: 8000/time-cookie-test.html. Figure 1 shows the result.
Listing 3. html demonstration of the filter
<! Doctype HTML public "-// W3C // dtd xhtml 1.0 transitional // en"
Http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
<HTML xmlns = "http://www.w3.org/1999/xhtml">
<Head>
<Title> Apache 2.0 time cookie filter test (*** time-Cookie ***) </title>
</Head>
<Body bgcolor = "# ffffff" text = "#000000" link = "# 0000ff"
Vlink = "#000080" alink = "# ff0000">
<H3> Apache HTTP Server 2.0 version <H1> Apache 2.0 time cookie filter test <P> This page request was generated on *** time-Cookie *** </P>
<P> -- uche ogbuji </P>
</Body>
</Html> Figure 1. html page rendered by time cookie filter (Figure omitted)
Module
The filter only modifies the data generated by other components. To compile an Apache extension that generates the original content based on the HTTP request, you need to write the module. The well-known modules include built-in modules for providing services for simple files, Apache modules for servers, mod_cgi for public Gateway Interface scripts, and mod_perl and mod_python for effective and direct calls to the scripting language.. Writing a module is more complex than writing a filter. However, since filters meet the needs of so many extensions, it is helpful for Apache 2.0 to divide them into simple APIs.
The module registers the hook to the server using the same ap_module_declare_data structure. The entry point of the main processing program is simpler. It only receives one request record from the HTTP message. The following is an example from mod_python 3.0, Which is customized for Apache 2.0:
Static int pythonhandler (request_rec * req ){
/* Handler stuff */
} A module is usually registered to process special file types in the configuration file or other such standards. This involves the use of a "magic string" to identify the specified for special tags
<Directory/pydir>
Addhandler Python-program. py
Pythonhandler script
</Directory> the preceding statements indicate the values in the/pydir path. requests with The py extension will be processed by the handler that understands the magic string "Python-program" (in other words, mod_python. The third line is a special configuration pseudocommand, which is defined and processed by the mod_python module at server startup (while the module is called by processing the registration hook of the configuration file ). Apache calls all the processing programs for each request, so each processing program should quickly decide whether the request is directed at it. Therefore, most header files start with a statement similar to the following:
If (! REQ-> handler | strcmp (req-> handler, "Python-program "))
Return declined; the preceding statement checks whether the magic handler string related to the path and other standard requests matches the magic string understood by mod_python. If they do not match, the request is rejected.
The basic knowledge required by most of the other writing modules is the same as that required by the writing filter. Create a bucket queue and add content to it when the queue is generated. You can also use the ap_pass_brigade function at the time of completion, but the receiver parameter you pass to it is r-> output_filters. Assume that you call the request record structure R. Apache then takes over the tasks that run content through the filter.
Start working and write your own things
Apache 2.0 APIs obviously benefit from long-term consideration and design by Apache developers. It is far less casual than 1.3 APIs, and it is quite coherent. Most common tasks of module writers are provided in functions, and resource management is much simpler than many other C APIs. The main problem is that the documentation is not complete enough and it is very difficult to generate them. At least hope that it will become better over time. On the other hand, poor API design won't be improved over time, so Apache solves the most important problem.
I think the process of compiling the 2.0 module is easier than that of compiling the 1.x module. You should also dare to give it a try. Starting with the filter, it must be a good way to warm up. Therefore, I suggest writing a prototype filter variant for any module project you may have if possible. Although I only discussed the output filter, other types are very similar. If you really want to write a module, you may need to study the mod_asis source code, which is provided along with Apache as an excellent and simple example (modules/generators/mod_asis.c ).
Because the Apache 2.0 API has been improved, if you find that Apache cannot meet all your needs, you do not have to worry too much. Use your favorite search engine to perform a search. If you are sure that no one has completed your task, please develop your own Apache extension.
Related Articles:
Http://httpd.apache.org/docs/2.0/developer/
Http://www.onlamp.com/pub/ct/38