Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright © 1999 by O'Reilly & Associates, Inc.


 


   Show Contents   Previous Page   Next Page

Chapter 2 - A First Module
"Hello World" with the C API

In this section...

Introduction
Building a Dynamically Loadable Module
Building Large C Modules
Building C Modules in the Windows Environment

Introduction

   Show Contents   Go to Top   Previous Page   Next Page

In this section we will create the same "Hello World" module using the C API. This will show you how closely related the two APIs really are. Many of the details in this section are specific for Unix versions of Apache. For differences relating to working in Win32 environments, be sure to read the section "Building C Modules in the Windows Environment."

The preparation for writing C API modules is somewhat simpler than that for the Perl modules. You just need to create a subdirectory in the Apache source tree to hold your site-specific source code. We recommend creating a directory named site in the modules subdirectory. The complete path to the directory will be something like ~www/src/modules/site (C:\Apache\src\modules\site on Win32 systems).

To have this new subdirectory participate in the server build process, create a file within it named Makefile.tmpl. For simple modules that are contained within a single source file, Makefile.tmpl can be completely empty. The Apache configure script does a pretty good job of creating a reasonable default makefile. Makefile.tmpl is there to provide additional file and library dependencies that Apache doesn't know about.

The next step is to create the module itself. Example 2-2 shows the source for mod_hello. Create a file in the site subdirectory named mod_hello.c and type in the source code (or better yet, steal it from the source code listings in http://www.modperl.com/book/source/).

Example 2-2. A First C-Language Module 

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
/* file: mod_hello.c */
/* here's the content handler */
static int hello_handler(request_rec *r) {
 const char* hostname;
  r->content_type = "text/html";
 ap_send_http_header(r);
 hostname = ap_get_remote_host(r->connection,r->per_dir_config,REMOTE_NAME);
  ap_rputs("<HTML>\n"                           ,r);
 ap_rputs("<HEAD>\n"                           ,r);
 ap_rputs("<TITLE>Hello There</TITLE>\n"       ,r);
 ap_rputs("</HEAD>\n"                          ,r);
 ap_rputs("<BODY>\n"                           ,r);
 ap_rprintf(r,"<H1>Hello %s</H1>\n"            ,hostname);
 ap_rputs("Who would take this book seriously if the first example didn't\n",r); 
  ap_rputs("say \"hello world\"?\n"             ,r);
 ap_rputs("</BODY>\n"                          ,r);
 ap_rputs("</HTML>\n"                          ,r);
  return OK;
}
/* Make the name of the content handler known to Apache */
static handler_rec hello_handlers[] =
{
   {"hello-handler", hello_handler},
   {NULL}
};
/* Tell Apache what phases of the transaction we handle */
module MODULE_VAR_EXPORT hello_module =
{
   STANDARD_MODULE_STUFF,
  NULL,               /* module initializer                 */
  NULL,               /* per-directory config creator       */
  NULL,               /* dir config merger                  */
  NULL,               /* server config creator              */
  NULL,               /* server config merger               */
  NULL,               /* command table                      */
  hello_handlers,     /* [9]  content handlers              */
  NULL,               /* [2]  URI-to-filename translation   */
  NULL,               /* [5]  check/validate user_id        */
  NULL,               /* [6]  check user_id is valid *here* */
  NULL,               /* [4]  check access by host address  */
  NULL,               /* [7]  MIME type checker/setter      */
  NULL,               /* [8]  fixups                        */
   NULL,               /* [10] logger                        */
  NULL,               /* [3]  header parser                 */
  NULL,               /* process initialization             */
  NULL,               /* process exit/cleanup               */
  NULL                /* [1]  post read_request handling    */
};

We'll go into the sordid details on how this module works later. Essentially, all the real work is done in the content handler subroutine hello_handler() which accepts an Apache request record pointer as its argument and returns an integer result code. The subroutine first changes the content_type field of the request record to text/html, promising the remote browser that we will be producing an HTML document. It then calls the Apache ap_send_http_header() subroutine to send the HTTP header off.

The hello_handler() subroutine now fetches the DNS name of the remote host by calling the ap_get_remote_host() function. It passes various parts of the request record to the function and specifies that our preference is to retrieve the remote host's DNS using a single DNS lookup rather than a more secure (but slower) double lookup.7 We now build the HTML document using a series of calls to ap_rputs() and ap_rprintf(). These subroutines act just like puts() and printf(), but their output is funneled to the browser by way of the Apache server. When the document is finished, we return a status code of OK, indicating to Apache that execution was successful.

The rest of this module consists of bookkeeping. First we create a handler_rec array. As discussed in more detail later, this data structure is used to associate certain document types with the handler subroutines that process them. A document type can be referred to by certain magic MIME types, such as application/x-httpd-cgi, but more frequently it is just a handler name for use in Apache's AddHandler and SetHandler directives. In this module, we associate the subroutine hello_handler() with the handler name hello-handler. Although in theory a single module could declare several content handlers, in practice they usually declare only one.

After this is another data structure created using the module type definition. This data structure is essentially a list of the various phases of the Apache HTTP transaction (described in the next chapter), with empty slots where you can place your handlers for those phases.

In mod_hello we're only interested in handling the content generation part of the transaction, which happens to be the seventh slot in the structure but is the ninth phase to run. There's no rhyme or reason in order of the slots because new transaction phases were invented over time. The bracketed numbers in the slot comments indicate the order in which the handlers run, although as we explain in the next chapter, not all handlers are run for all transactions. We leave all the slots NULL except for the content handlers field, in which we place the address of the previously declared handler_rec array.

Now the new module needs to be configured with Apache. This can be accomplished with little effort thanks to Apache's configure script. The --activate-module argument is used to add third-party modules to the server, that is, modules not included with the Apache distribution. Its value is the path to the source or object file of the module to be included, in this case src/modules/site/mod_hello.c. Once activated, the --enable-module argument works just as it does with standard modules, in this case, linking mod_hello with the new server. From the top of the Apache distribution directory (which contains the ABOUT_APACHE file) type this command:

% ./configure --activate-module=src/modules/site/mod_hello.c \
             --enable-module=hello

Configuring for Apache, Version 1.3.3
+ activated hello module (modules/site/mod_hello.c)
Creating Makefile
Creating Configuration.apaci in src
Creating Makefile in src
+ configured for Linux platform
+ setting C compiler to gcc
+ adding selected modules
+ checking for system header files
+ doing sanity check on compiler and options
Creating Makefile in src/support
Creating Makefile in src/main
Creating Makefile in src/ap
Creating Makefile in src/regex
Creating Makefile in src/os/unix
Creating Makefile in src/modules/standard

You can now run make and a new httpd will be built. If you watch the build process carefully, you'll see mod_hello.c first compiled into an object file named mod_hello.o, and then added to a library archive named libsite.a. libsite.a, in turn, is statically linked into the httpd executable during the final link phase. If anything goes wrong during compilation and linking, you'll need to go back to see what you might have done wrong.

To test the module, you'll need to associate it with a URI. The simplest way to do this is to use SetHandler to map it to a part of the document tree. Add a <Location> directive to perl.conf (or one of the other configuration files) that looks like this:

<Location /hey/there>
  SetHandler  hello-handler
</Location>

Stop the Apache server if it is already running, and launch the new httpd. Better yet, you can keep the existing server running and just launch the new httpd with the -f flag to specify an alternate httpd.conf file. Be sure to change the Port directive in the alternate httpd.conf so that it listens on an unused port. Now fire up a browser and fetch the URI http://your.site/hey/there. You should get the same page that we saw in Figure 2-1.

When you want to make changes to the mod_hello.c source code, just edit the file and run make again. You only need to run configure when adding a new module or completely removing an old one. You won't break anything if you run configure when you don't need to, but you will cause the entire server to be recompiled from scratch, which might take a while.

   Show Contents   Go to Top   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.

HIVE: All information for read only. Please respect copyright!
Hosted by hive КГБ: Киевская городская библиотека