Home > Articles > Web Development > Perl

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

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 trouble—for 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_APACHEOBJ
    r = 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.

  • + Share This
  • 🔖 Save To Your Account