- Introduction
- The Apache Request Object
- The HTTP Request Message
- The Client Request
- Accessing Client Request Headers
- Accessing HTML Form Fields
- Reading POSTed Data Manually
- Manipulating Cookies
- Handling File Uploads
- Setting Server Response Headers
- Controlling Caching Behavior
- Sending Server Response Headers
- Setting the Response Status
- Setting Error Headers
- Manipulating Headers with Multiple Like Fields
- Using Subrequests
- Setting Headers for Subrequests
- Short-Circuiting Subrequests
- Getting or Setting the Request Method
- Accessing the Request Object from XS
Accessing the Request Object from XS
You need access to the request object from an XS subroutine.
Technique
Use h2xs to build the stub of the module, then follow these detailed instructions.
Comments
Although Perl is a wonderful language, the extra effort needed to write an XS-based subroutine is sometimes worth the troublefor instance, when you have intense calculations that are better geared toward C, or when you can take advantage of a particular third-party function to perform the task at hand. We describe here some special considerations that you need to take into account if you want to have access to the Apache request object within XS routines.
The example we consider is an overly simple one, but it does have its utility in illustrating a few techniques as well as some interesting history. Although mod_perl provides access to nearly all the fields of the Apache request record, there are a few that mod_perl does not offer any method for, and thus are not accessible in your Perl handlers. The assbackwards flag in the request record is used to note whether the client is making a Simple-Request, which was allowed by the 0.9 version of the HTTP protocol. You can simulate a Simple-Request by making a GET request that does not have a protocol version in the request line.
$ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /perl-status <html> <head><title>Apache::Status</title></head> <body> ...
If Apache sees that the request is "simple," it will set the assbackwards flag in the request record, which reminds Apache to send an appropriately formatted Simple-Response when it sends the content.
Because all modern browsers use at least HTTP version 1.0, the likelihood of having to use mod_perl to conform to an HTTP/0.9 request is negligible, and in fact Apache deals with this for us when it parses the request. However, one of the interesting things to notice about the preceding dialogue is the lack of server response headers. In fact, this is the definition of a Simple-Response.
In effect, Apache uses the assbackwards flag to determine whether the response is allowed to include headers. This is an interesting feature, and one that mod_perl effectively takes advantage of in implementing $sub->run(1). Internally, Apache sets assbackwards to 1 when running a subrequest in order to suppress header generation. When calling run(1), mod_perl actually sets assbackwards back to 0, which signals Apache to send the response headers where it otherwise wouldn't.
We can implement our own function to give us access to the assbackwards flag in the request record, which mod_perl doesn't directly provide. As with building any XS module, it is best to start off with h2xs:
$ h2xs -APn Cookbook::SimpleRequest Writing Cookbook/SimpleRequest/SimpleRequest.pm Writing Cookbook/SimpleRequest/SimpleRequest.xs Writing Cookbook/SimpleRequest/Makefile.PL Writing Cookbook/SimpleRequest/test.pl Writing Cookbook/SimpleRequest/Changes Writing Cookbook /SimpleRequest/MANIFEST
This will create stubs for most of the files needed to build the module Cookbook::SimpleRequest. The first step is to edit the module file SimpleRequest.pm to add the name of our XS routine to @EXPORT_OK, following the good programming practice of not exporting any symbols by default. For our SimpleRequest.pm we take some liberties with the look of DynaLoader's bootstrap() method in our edits, but the end result is the same as provided by the default .pm file.
Listing 3.1 SimpleRequest.pm
package Cookbook::SimpleRequest; use 5.006; use strict; use warnings; require Exporter; require DynaLoader; our @ISA = qw(Exporter DynaLoader); our @EXPORT_OK = qw(assbackwards); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); 1;
The next file, SimpleRequest.xs, requires substantial modification.
Listing 3.2 SimpleRequest.xs
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "mod_perl.h" #include "mod_perl_xs.h" MODULE = Cookbook::SimpleRequest PACKAGE = Cookbook::SimpleRequest PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL
This defines the function assbackwards(), which allows us to either retrieve the current value of assbackwards from the request record or set it to an integer value. Note that, in addition to the standard XS header files EXTERN.h, perl.h, and XSUB.h, we have included mod_perl.h which, in turn, will pull in any needed Apache header files. We also included mod_perl_xs.h, which defines some useful macros like get_set_IV, which does the dirty work for us.
The request record r in SimpleRequest.xs is of type Apache, which is not a data type that Perl understands on its own; The Apache type needs to be defined through a separate typemap file, which gives the rules for converting data types between C and Perl. So, we also need to create a file named typemap and drop in the following code:
Listing 3.3 typemap for Cookbook::SimpleRequest
TYPEMAP Apache T_APACHEOBJ OUTPUT T_APACHEOBJ sv_setref_pv($arg, \"${ntype}\", (void*)$var); INPUT T_APACHEOBJr = sv2request_rec($arg, \"$ntype\", cv);
Finally, we come to Makefile.PL which will be used to build and install the module, and which also requires significant modification.
Listing 3.4 Makefile.PL for Cookbook::SimpleRequest
#!perl use ExtUtils::MakeMaker; use Apache::src (); use Config; use strict; my %config; $config{INC} = Apache::src->new->inc; if ($^O =~ /Win32/) { require Apache::MyConfig; $config{DEFINE} = ' -D_WINSOCK2API_ -D_MSWSOCK_ '; $config{DEFINE} .= ' -D_INC_SIGNAL -D_INC_MALLOC ' if $Config{usemultiplicity}; $config{LIBS} = qq{ -L"$Apache::MyConfig::Setup{APACHE_LIB}" -lApacheCore } . qq{ -L"$Apache::MyConfig::Setup{MODPERL_LIB}" -lmod_perl}; } WriteMakefile( NAME => 'Cookbook::SimpleRequest', VERSION_FROM => 'SimpleRequest.pm', PREREQ_PM => { mod_perl => 1.26 }, ABSTRACT => 'An XS-based Apache module', AUTHOR => 'authors@modperlcookbook.org', %config, );
This Makefile.PL, although complex, accomplishes a number of tasks that are necessary to tie everything together. It
Sets the include directories for finding header files through Apache::src->new->inc()
Sets the needed library directories and libraries for Win32, through the special hash %Apache::MyConfig::Setup
Sets some needed compiler flags for Win32
Sets PREREQ_PM to mod_perl (version 1.26 or greater), so that a warning will be given if this version of mod_perl is not present
Defines the ABSTRACT and AUTHOR used in making ppd files for ActiveState-like binary distributions
At this point, we are ready to go through the standard build procedure:
$ perl Makefile.PL Checking if your kit is complete... Looks good Writing Makefile for Cookbook::SimpleRequest $ make cp SimpleRequest.pm blib/lib/Cookbook/SimpleRequest.pm /usr/local/bin/perl -I/usr/local/lib/perl5/5.6.1/i686-linux-thread-multi -I/ usr/local/lib/perl5/5.6.1 /usr/local/lib/perl5/5.6.1/ExtUtils/xsubpp -typemap /usr/local/lib/perl5/5.6.1/ ExtUtils/typemap -typemap typemap SimpleRequest.xs > SimpleRequest.xsc && mv SimpleRequest.xsc SimpleRequest.c ... chmod 755 blib/arch/auto/Cookbook/SimpleRequest/SimpleRequest.so cp SimpleRequest.bs blib/arch/auto/Cookbook/SimpleRequest/SimpleRequest.bs chmod 644 blib/arch/auto/Cookbook/SimpleRequest/SimpleRequest.bs $ su Password: # make install Installing /usr/local/lib/perl5/site_perl/5.6.1/i686-linux-thread-multi/auto/Cookbook/ SimpleRequest/SimpleRequest.so Installing /usr/local/lib/perl5/site_perl/5.6.1/i686-linux-thread-multi/auto/Cookbook/ SimpleRequest/SimpleRequest.bs Files found in blib/arch: installing files in blib/lib into architecture dependent library tree Installing /usr/local/lib/perl5/site_perl/5.6.1/i686-linux-thread-multi/ Cookbook/SimpleRequest.pm Writing /usr/local/lib/perl5/site_perl/5.6.1/i686-linux-thread-multi/auto/Cookbook/ SimpleRequest/.packlist Appending installation info to /usr/local/lib/perl5/5.6.1/i686-linux-thread-multi/perllocal.pod
After all this elaborate preparation, the use of this module is a little anticlimatic; we simply make up a handler that uses Cookbook::SimpleRequest in the standard way:
package Cookbook::SimpleTest; use Apache::Constants qw(OK); use Cookbook::SimpleRequest qw(assbackwards); use strict; sub handler { my $r = shift; # Get the old value and set the current value # to supress the headers. my $old = assbackwards($r, 1); # Verify the new value. my $new = assbackwards($r); $r->send_http_header('text/plain'); $r->print("look ma, no headers!\n"); $r->print("old: $old, new $new\n"); return OK; } 1;
Although this example doesn't do anything terribly useful, it does illustrate a general framework for constructing practical XS-based modules that use the Apache request object.
As we mentioned at the start, there are times when it is preferable or necessary to write a Perl interface to C routines. However, before you go off and implement a new method for some particular function that mod_perl seems to be missing, take a look through the Apache C API and try to find the functionality there. In addition to the request and related records, the Apache C API provides a number of public ap_* routines that you can hook into. Some of these are for convenience, but others should be used in preference to the corresponding data in the appropriate record.