mod_perl: do more) Geoffrey Young [email protected] http://www.modperlcookbook.org/ 1 3 hours of mod_perl stuff ® rejected by OSCon Geoffrey Young [email protected] http://www.modperlcookbook.org/ 2 http://www.modperlcookbook.org/ 3 Just the facts http://www.modperlcookbook.org/ 4 Just the facts • OSCon theme this year is doing more with less http://www.modperlcookbook.org/ 5 Just the facts • OSCon theme this year is doing more with less • If you ask Joe Developer what mod_perl is, he quacks "It's basically just faster CGI" http://www.modperlcookbook.org/ 6 Just the facts • OSCon theme this year is doing more with less • If you ask Joe Developer what mod_perl is, he quacks "It's basically just faster CGI" • That's not exactly right... http://www.modperlcookbook.org/ 7 Request Phases http://www.modperlcookbook.org/ 8 Request Phases • Apache breaks down request processing into separate, logical parts called phases http://www.modperlcookbook.org/ 9 Request Phases • Apache breaks down request processing into separate, logical parts called phases client request logging content fixups MIME setting http://www.modperlcookbook.org/ URI-based init URI translation file-based init resource control 10 Request Phases • Apache breaks down request processing into separate, logical parts called phases • Each request is stepped through the phases in turn until... http://www.modperlcookbook.org/ 11 Request Phases • Apache breaks down request processing into separate, logical parts called phases • Each request is stepped through the phases in turn until... – all processing is complete http://www.modperlcookbook.org/ 12 Request Phases • Apache breaks down request processing into separate, logical parts called phases • Each request is stepped through the phases in turn until... – all processing is complete – somebody throws an "error" http://www.modperlcookbook.org/ 13 So What? • Most Apache users don't worry about the request cycle too much http://www.modperlcookbook.org/ 14 So What? • Most Apache users don't worry about the request cycle too much... • ...but they do use modules that plug into it http://www.modperlcookbook.org/ 15 ... for instance client request URI-based init mod_rewrite: URI translation RewriteRule /favicon.ico$ /images/favicon.ico http://www.modperlcookbook.org/ 16 ... for instance client request URI-based init URI translation file-based init mod_auth: resource control AuthUserFile .htpasswd http://www.modperlcookbook.org/ 17 ... for instance client request mod_cgi: URI-based init SetHandler cgi-script content fixups MIME setting http://www.modperlcookbook.org/ URI translation file-based init resource control 18 That's great, but… • Breaking down the request into distinct phases has many benefits http://www.modperlcookbook.org/ 19 That's great, but… • Breaking down the request into distinct phases has many benefits – gives each processing point a role that can be easily managed and programmed http://www.modperlcookbook.org/ 20 That's great, but… • Breaking down the request into distinct phases has many benefits – gives each processing point a role that can be easily managed and programmed – makes Apache more like an application framework rather than a content engine http://www.modperlcookbook.org/ 21 That's great, but... • Breaking down the request into distinct phases has many benefits – gives each processing point a role that can be easily managed and programmed – makes Apache more like an application framework rather than a content engine • but you have to code in C http://www.modperlcookbook.org/ 22 Enter mod_perl http://www.modperlcookbook.org/ 23 Enter mod_perl • mod_perl embeds a perl interpreter directly within the Apache runtime http://www.modperlcookbook.org/ 24 Enter mod_perl • mod_perl embeds a perl interpreter directly within the Apache runtime – opens up the Apache API to Perl code http://www.modperlcookbook.org/ 25 Enter mod_perl • mod_perl embeds a perl interpreter directly within the Apache runtime – opens up the Apache API to Perl code – offers an interface to each phase of the request cycle http://www.modperlcookbook.org/ 26 Enter mod_perl • mod_perl embeds a perl interpreter directly within the Apache runtime – opens up the Apache API to Perl code – offers an interface to each phase of the request cycle • We like Perl http://www.modperlcookbook.org/ 27 Registry is just a handler http://www.modperlcookbook.org/ 28 Registry is just a handler • Apache::Registry is merely a set of (incredibly clever) Perl subroutines that interact with the request cycle http://www.modperlcookbook.org/ 29 Registry is just a handler • Apache::Registry is merely a set of (incredibly clever) Perl subroutines that interact with the request cycle • Performance gains are made possible due to what mod_perl really is http://www.modperlcookbook.org/ 30 Registry is just a handler • Apache::Registry is merely a set of (incredibly clever) Perl subroutines that interact with the request cycle • Performance gains are made possible due to what mod_perl really is • Let's take a peek inside... http://www.modperlcookbook.org/ 31 Apache::Registry http://www.modperlcookbook.org/ 32 Apache::Registry • Client side – http://localhost/perl-bin/bar.pl http://www.modperlcookbook.org/ 33 Apache::Registry • Client side – http://localhost/perl-bin/bar.pl • Server side http://www.modperlcookbook.org/ 34 Apache::Registry • Client side – http://localhost/perl-bin/bar.pl • Server side – mod_perl intercepts content generation http://www.modperlcookbook.org/ 35 Apache::Registry • Client side – http://localhost/perl-bin/bar.pl • Server side – mod_perl intercepts content generation – searches @INC for Apache/Registry.pm http://www.modperlcookbook.org/ 36 Apache::Registry • Client side – http://localhost/perl-bin/bar.pl • Server side – mod_perl intercepts content generation – searches @INC for Apache/Registry.pm – calls Apache::Registry::handler(Apache->request) http://www.modperlcookbook.org/ 37 Apache::Registry • Client side – http://localhost/perl-bin/bar.pl • Server side – mod_perl intercepts content generation – searches @INC for Apache/Registry.pm – calls Apache::Registry::handler(Apache->request) – inserts wizardry http://www.modperlcookbook.org/ 38 Apache::Registry • Client side – http://localhost/perl-bin/bar.pl • Server side – mod_perl intercepts content generation – searches @INC for Apache/Registry.pm – calls Apache::Registry::handler(Apache->request) – inserts wizardry – returns response to client (a la CGI) http://www.modperlcookbook.org/ 39 Wizardry, you say? http://www.modperlcookbook.org/ 40 Wizardry, you say? • The wizardry is basically just putting the CGI script into it's own package http://www.modperlcookbook.org/ 41 Wizardry, you say? • The wizardry is basically just putting the CGI script into it's own package package Apache::ROOT::perl_2dbin::foo_2epl; sub handler { BEGIN { $^W = 1; }; $^W = 1; ... your script here... } 1; http://www.modperlcookbook.org/ 42 Wizardry, you say? • The wizardry is basically just putting the CGI script into it's own package package Apache::ROOT::perl_2dbin::foo_2epl; sub handler { BEGIN { $^W = 1; }; $^W = 1; ... your script here... } 1; http://www.modperlcookbook.org/ 43 Wizardry, you say? • The wizardry is basically just putting the CGI script into it's own package package Apache::ROOT::perl_2dbin::foo_2epl; sub handler { BEGIN { $^W = 1; }; $^W = 1; ... your script here... } 1; http://www.modperlcookbook.org/ 44 Wizardry, you say? • The wizardry is basically just putting the CGI script into it's own package package Apache::ROOT::perl_2dbin::foo_2epl; sub handler { BEGIN { $^W = 1; }; $^W = 1; ... your script here... } 1; http://www.modperlcookbook.org/ 45 Wizardry, you say? • The wizardry is basically just putting the CGI script into it's own package package Apache::ROOT::perl_2dbin::foo_2epl; sub handler { BEGIN { $^W = 1; }; $^W = 1; ... your script here... } 1; • Because the perl interpreter is persistent within Apache the (compiled) package is already in memory when called http://www.modperlcookbook.org/ 46 Do more with less? http://www.modperlcookbook.org/ 47 Do more with less? • Most developer's haven't even scratched the surface of what's possible with mod_perl http://www.modperlcookbook.org/ 48 Do more with less? • Most developer's haven't even scratched the surface of what's possible with mod_perl • Almost 25% of Apache servers are running mod_perl http://www.modperlcookbook.org/ 49 Do more with less? • Most developer's haven't even scratched the surface of what's possible with mod_perl • Almost 25% of Apache servers are running mod_perl • Why do more with less? http://www.modperlcookbook.org/ 50 Do more with less? • Most developer's haven't even scratched the surface of what's possible with mod_perl • Almost 25% of Apache servers are running mod_perl • Why do more with less? • Do more with what you already have http://www.modperlcookbook.org/ 51 Do more • mod_perl allows you to interact with and directly alter server behavior http://www.modperlcookbook.org/ 52 Do more • mod_perl allows you to interact with and directly alter server behavior • Gives you the ability to "program within Apache's framework instead of around it" http://www.modperlcookbook.org/ 53 Do more • mod_perl allows you to interact with and directly alter server behavior • Gives you the ability to "program within Apache's framework instead of around it" • Allows you to intercept basic Apache functions and replace them with your own (sometimes devious) Perl substitutes http://www.modperlcookbook.org/ 54 Do more • mod_perl allows you to interact with and directly alter server behavior • Gives you the ability to "program within Apache's framework instead of around it" • Allows you to intercept basic Apache functions and replace them with your own (sometimes devious) Perl substitutes • Lets you do it all in Perl instead of C http://www.modperlcookbook.org/ 55 For Instance... • With mod_perl, it's easy to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle http://www.modperlcookbook.org/ 56 For Instance... • With mod_perl, it's easy to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle http://www.modperlcookbook.org/ 57 HTTP/1.0 and Host http://www.modperlcookbook.org/ 58 HTTP/1.0 and Host • HTTP/1.0 does not require a Host header http://www.modperlcookbook.org/ 59 HTTP/1.0 and Host • HTTP/1.0 does not require a Host header • assumes a "one host per IP" configuration http://www.modperlcookbook.org/ 60 HTTP/1.0 and Host • HTTP/1.0 does not require a Host header • assumes a "one host per IP" configuration • this limitation breaks name-based virtual host servers for browsers that follow HTTP/1.0 to the letter http://www.modperlcookbook.org/ 61 HTTP/1.0 and Host • HTTP/1.0 does not require a Host header • assumes a "one host per IP" configuration • this limitation breaks name-based virtual host servers for browsers that follow HTTP/1.0 to the letter – most send the Host header, so all is well http://www.modperlcookbook.org/ 62 $ telnet www.apache.org 80 Trying 63.251.56.142... Connected to www.apache.org. Escape character is '^]'. GET /foo.html HTTP/1.0 http://www.modperlcookbook.org/ 63 $ telnet www.apache.org 80 Trying 63.251.56.142... Connected to www.apache.org. Escape character is '^]'. GET /foo.html HTTP/1.0 http://www.modperlcookbook.org/ 64 $ telnet www.apache.org 80 Trying 63.251.56.142... Connected to www.apache.org. Escape character is '^]'. GET /foo.html HTTP/1.0 HTTP/1.1 302 Found Date: Tue, 04 Jun 2002 00:52:55 GMT Server: Apache/2.0.37-dev (Unix) Location: http://httpd.apache.org/dev/ Content-Length: 289 Connection: close Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>302 Found</title> </head><body> <h1>Found</h1> <p>The document has moved <a href="http://httpd.apache.org/dev/">here</a>.</p> <hr /> <address>Apache/2.0.37-dev Server at dev.apache.org Port 80</address> </body></html> http://www.modperlcookbook.org/ 65 $ telnet www.apache.org 80 Trying 63.251.56.142... Connected to www.apache.org. Escape character is '^]'. GET /foo.html HTTP/1.0 Host: www.apache.org http://www.modperlcookbook.org/ 66 $ telnet www.apache.org 80 Trying 63.251.56.142... Connected to www.apache.org. Escape character is '^]'. GET /foo.html HTTP/1.0 Host: www.apache.org http://www.modperlcookbook.org/ 67 $ telnet www.apache.org 80 Trying 63.251.56.142... Connected to www.apache.org. Escape character is '^]'. GET /foo.html HTTP/1.0 Host: www.apache.org http://www.modperlcookbook.org/ 68 $ telnet www.apache.org 80 Trying 63.251.56.142... Connected to www.apache.org. Escape character is '^]'. GET /foo.html HTTP/1.0 Host: www.apache.org HTTP/1.1 404 Not Found Date: Tue, 04 Jun 2002 00:56:40 GMT Server: Apache/2.0.37-dev (Unix) Content-Length: 283 Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL /foo.html was not found on this server.</p> <hr /> <address>Apache/2.0.37-dev Server at www.apache.org Port 80</address> </body></html> http://www.modperlcookbook.org/ 69 Let's fix it http://www.modperlcookbook.org/ 70 Let's fix it • Intercept every request prior to content-generation and return an error unless... http://www.modperlcookbook.org/ 71 Let's fix it • Intercept every request prior to content-generation and return an error unless... – there is a Host header http://www.modperlcookbook.org/ 72 Let's fix it • Intercept every request prior to content-generation and return an error unless... – there is a Host header – the request is an absolute URI http://www.modperlcookbook.org/ 73 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 74 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 75 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 76 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 77 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 78 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 79 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 80 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 81 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 82 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 83 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 84 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 85 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 86 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 87 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 88 package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; # Valid requests for name based virtual hosting are: # requests with a Host header, or # requests that are absolute URIs. unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 89 Setup • add TrapNoHost.pm to @INC http://www.modperlcookbook.org/ 90 Setup • add TrapNoHost.pm to @INC ServerRoot/lib/perl/Cookbook/TrapNoHost.pm http://www.modperlcookbook.org/ 91 Setup • add TrapNoHost.pm to @INC ServerRoot/lib/perl/Cookbook/TrapNoHost.pm • add to httpd.conf http://www.modperlcookbook.org/ 92 Setup • add TrapNoHost.pm to @INC ServerRoot/lib/perl/Cookbook/TrapNoHost.pm • add to httpd.conf PerlModule Cookbook::TrapNoHost http://www.modperlcookbook.org/ 93 Setup • add TrapNoHost.pm to @INC ServerRoot/lib/perl/Cookbook/TrapNoHost.pm • add to httpd.conf PerlModule Cookbook::TrapNoHost PerlTransHandler Cookbook::TrapNoHost http://www.modperlcookbook.org/ 94 Setup • add TrapNoHost.pm to @INC ServerRoot/lib/perl/Cookbook/TrapNoHost.pm • add to httpd.conf PerlModule Cookbook::TrapNoHost PerlTransHandler Cookbook::TrapNoHost • that's it! http://www.modperlcookbook.org/ 95 Apache Request Cycle client request logging content fixups MIME setting http://www.modperlcookbook.org/ URI-based init URI translation file-based init resource control 96 Intercept the Request client request URI-based init PerlTransHandler http://www.modperlcookbook.org/ 97 Intercept the Request client request URI-based init PerlTransHandler HTTP/1.1 400 Bad Request Date: Tue, 04 Jun 2002 01:17:52 GMT Server: Apache/1.3.25-dev (Unix) mod_perl/1.27_01-dev Perl/v5.8.0 Connection: close Content-Type: text/html; charset=iso-8859-1 Oops! Did you mean to omit a Host header? http://www.modperlcookbook.org/ 98 Intercept the Request client request URI-based init PerlTransHandler HTTP/1.1 400 Bad Request Date: Tue, 04 Jun 2002 01:17:52 GMT Server: Apache/1.3.25-dev (Unix) mod_perl/1.27_01-dev Perl/v5.8.0 Connection: close Content-Type: text/html; charset=iso-8859-1 Oops! Did you mean to omit a Host header? http://www.modperlcookbook.org/ 99 Intercept the Request client request logging URI-based init PerlTransHandler http://www.modperlcookbook.org/ 100 http://www.modperlcookbook.org/ 101 What else? http://www.modperlcookbook.org/ 102 Modularize Processing http://www.modperlcookbook.org/ 103 Modularize Processing • Parsing out the query string or POST data on each request is a pain http://www.modperlcookbook.org/ 104 Modularize Processing • Parsing out the query string or POST data on each request is a pain • For the most part, you know you need it for every request to a given <Location> http://www.modperlcookbook.org/ 105 Modularize Processing • Parsing out the query string or POST data on each request is a pain • For the most part, you know you need it for every request to a given <Location> • Take advantage of the request cycle http://www.modperlcookbook.org/ 106 Modularize Processing • Parsing out the query string or POST data on each request is a pain • For the most part, you know you need it for every request to a given <Location> • Take advantage of the request cycle • Modularize the parsing code http://www.modperlcookbook.org/ 107 Apache::RequestNotes http://www.modperlcookbook.org/ 108 Apache::RequestNotes • Apache::RequestNotes parses cookies and input parameters http://www.modperlcookbook.org/ 109 Apache::RequestNotes • Apache::RequestNotes parses cookies and input parameters • stores the data in pnotes() for later retrieval http://www.modperlcookbook.org/ 110 Setup http://www.modperlcookbook.org/ 111 Setup Alias /perl-bin /usr/local/apache/perl-bin <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlInitHandler Apache::RequestNotes </Location> http://www.modperlcookbook.org/ 112 Setup Alias /perl-bin /usr/local/apache/perl-bin <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlInitHandler Apache::RequestNotes </Location> http://www.modperlcookbook.org/ 113 <Location> Processing client request URI-based init URI translation PerlInitHandler http://www.modperlcookbook.org/ 114 <Location> Processing client request URI-based init URI translation PerlInitHandler my $input = $r->pnotes('INPUT'); # Apache::Table reference my $uploads = $r->pnotes('UPLOADS'); # Apache::Upload array ref my $cookies = $r->pnotes('COOKIES'); # hash reference http://www.modperlcookbook.org/ 115 User Authentication http://www.modperlcookbook.org/ 116 User Authentication • Some .htaccess file or httpd.conf http://www.modperlcookbook.org/ 117 User Authentication • Some .htaccess file or httpd.conf AuthUserFile .htpasswd AuthName "my site" AuthType Basic Require valid-user http://www.modperlcookbook.org/ 118 User Authentication • Some .htaccess file or httpd.conf AuthUserFile .htpasswd AuthName "my site" AuthType Basic Require valid-user geoff:zzpEyL0tbgwwk http://www.modperlcookbook.org/ 119 User Authentication • Some .htaccess file or httpd.conf AuthUserFile .htpasswd AuthName "my site" AuthType Basic Require valid-user geoff:zzpEyL0tbgwwk http://www.modperlcookbook.org/ 120 User Authentication • Some .htaccess file or httpd.conf AuthUserFile .htpasswd AuthName "my site" AuthType Basic Require valid-user http://www.modperlcookbook.org/ 121 Who Uses Flat Files? http://www.modperlcookbook.org/ 122 Who Uses Flat Files? • flat files are limiting, hard to manage, difficult to integrate, and just plain boring http://www.modperlcookbook.org/ 123 Who Uses Flat Files? • flat files are limiting, hard to manage, difficult to integrate, and just plain boring • we can do more and replace flat files with our own authentication mechanism http://www.modperlcookbook.org/ 124 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 125 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 126 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 127 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 128 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 129 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 130 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 131 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 132 package My::Authenticate; use Apache::Constants qw(OK AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Get the client-supplied credentials. my ($status, $password) = $r->get_basic_auth_pw; return $status unless $status == OK; # Perform some custom user/password validation. return OK if authenticate_user($r->user, $password); # Whoops, bad credentials. $r->note_basic_auth_failure; return AUTH_REQUIRED; } http://www.modperlcookbook.org/ 133 Configuration http://www.modperlcookbook.org/ 134 Configuration • change AuthUserFile .htpasswd AuthName "my site" AuthType Basic Require valid-user http://www.modperlcookbook.org/ 135 Configuration • change AuthUserFile .htpasswd AuthName "my site" AuthType Basic Require valid-user • to PerlAuthenHandler My::Authenticate AuthName "cookbook" AuthType Basic Require valid-user http://www.modperlcookbook.org/ 136 Configuration • change AuthUserFile .htpasswd AuthName "my site" AuthType Basic Require valid-user • to PerlAuthenHandler My::Authenticate AuthName "cookbook" AuthType Basic Require valid-user http://www.modperlcookbook.org/ 137 Authentication client request URI-based init URI translation file-based init PerlAuthenHandler http://www.modperlcookbook.org/ 138 Authentication client request URI-based init URI translation file-based init PerlAuthenHandler HTTP/1.1 401 Authorization Required http://www.modperlcookbook.org/ 139 Authentication client request logging URI-based init URI translation file-based init PerlAuthenHandler http://www.modperlcookbook.org/ 140 The Choice is Yours • How you decide to authenticate is now up to you http://www.modperlcookbook.org/ 141 The Choice is Yours • How you decide to authenticate is now up to you sub authenticate_user { my ($user, $pass) = @_; return $user eq $pass; } http://www.modperlcookbook.org/ 142 The Choice is Yours • How you decide to authenticate is now up to you sub authenticate_user { my ($user, $pass) = @_; return $user eq $pass; } • Are you seeing the possibilities yet? http://www.modperlcookbook.org/ 143 The Power of CPAN • Over 25 Apache:: shrink-wrapped modules on CPAN for authentication – SecureID – Radius – SMB – LDAP – NTLM http://www.modperlcookbook.org/ 144 What else? http://www.modperlcookbook.org/ 145 Logging • Apache's default is to use mod_log_config in common format http://www.modperlcookbook.org/ 146 Logging • Apache's default is to use mod_log_config in common format LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog logs/access_log common http://www.modperlcookbook.org/ 147 Logging • Apache's default is to use mod_log_config in common format LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog logs/access_log common • Most people tweak this to combined http://www.modperlcookbook.org/ 148 Logging • Apache's default is to use mod_log_config in common format LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog logs/access_log common • Most people tweak this to combined LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined http://www.modperlcookbook.org/ 149 Logging to a Database http://www.modperlcookbook.org/ 150 Logging to a Database • In general, people in marketing don't know what they want when it comes to reports http://www.modperlcookbook.org/ 151 Logging to a Database • In general, people in marketing don't know what they want when it comes to reports • Logging directly to a database makes life easier if you have an application for which you need lots of reports http://www.modperlcookbook.org/ 152 Logging to a Database • In general, people in marketing don't know what they want when it comes to reports • Logging directly to a database makes life easier if you have an application for which you need lots of reports • Incurs very little overhead if you are already using a database in your application http://www.modperlcookbook.org/ 153 package Cookbook::SiteLog; use Apache::Constants qw(OK); use DBI; use strict; sub handler { my $r = shift; my $dbh = DBI->connect($r->dir_config('DBASE'), {RaiseError => 1, AutoCommit => 1, PrintError => 1}) or die $DBI::errstr; my %columns = ( status bytes language ); => $r->status, => $r->bytes_sent, => $r->headers_in->get('Accept-Language'), my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into www.sitelog (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) ); my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1; http://www.modperlcookbook.org/ 154 package Cookbook::SiteLog; use Apache::Constants qw(OK); use DBI; use strict; sub handler { my $r = shift; my $dbh = DBI->connect($r->dir_config('DBASE'), {RaiseError => 1, AutoCommit => 1, PrintError => 1}) or die $DBI::errstr; my %columns = ( status bytes language ); => $r->status, => $r->bytes_sent, => $r->headers_in->get('Accept-Language'), my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into www.sitelog (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) ); my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1; http://www.modperlcookbook.org/ 155 package Cookbook::SiteLog; use Apache::Constants qw(OK); use DBI; use strict; sub handler { my $r = shift; my $dbh = DBI->connect($r->dir_config('DBASE'), {RaiseError => 1, AutoCommit => 1, PrintError => 1}) or die $DBI::errstr; my %columns = ( status bytes language ); => $r->status, => $r->bytes_sent, => $r->headers_in->get('Accept-Language'), my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into www.sitelog (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) ); my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1; http://www.modperlcookbook.org/ 156 package Cookbook::SiteLog; use Apache::Constants qw(OK); use DBI; use strict; sub handler { my $r = shift; my $dbh = DBI->connect($r->dir_config('DBASE'), {RaiseError => 1, AutoCommit => 1, PrintError => 1}) or die $DBI::errstr; my %columns = ( status bytes language ); => $r->status, => $r->bytes_sent, => $r->headers_in->get('Accept-Language'), my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into www.sitelog (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) ); my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1; http://www.modperlcookbook.org/ 157 package Cookbook::SiteLog; use Apache::Constants qw(OK); use DBI; use strict; sub handler { my $r = shift; my $dbh = DBI->connect($r->dir_config('DBASE'), {RaiseError => 1, AutoCommit => 1, PrintError => 1}) or die $DBI::errstr; my %columns = ( status bytes language ); => $r->status, => $r->bytes_sent, => $r->headers_in->get('Accept-Language'), my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into www.sitelog (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) ); my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1; http://www.modperlcookbook.org/ 158 package Cookbook::SiteLog; use Apache::Constants qw(OK); use DBI; use strict; sub handler { my $r = shift; my $dbh = DBI->connect($r->dir_config('DBASE'), {RaiseError => 1, AutoCommit => 1, PrintError => 1}) or die $DBI::errstr; my %columns = ( status bytes language ); => $r->status, => $r->bytes_sent, => $r->headers_in->get('Accept-Language'), my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into www.sitelog (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) ); my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1; http://www.modperlcookbook.org/ 159 Logging client request PerlLogHandler content fixups MIME setting http://www.modperlcookbook.org/ URI-based init URI translation file-based init resource control 160 Speaking of Databases http://www.modperlcookbook.org/ 161 Speedy DBI • Apache::DBI rocks http://www.modperlcookbook.org/ 162 Speedy DBI • Apache::DBI rocks • Reduces connection overhead http://www.modperlcookbook.org/ 163 Speedy DBI • Apache::DBI rocks • Reduces connection overhead • "Works like magic" http://www.modperlcookbook.org/ 164 Speedy DBI • Apache::DBI rocks • Reduces connection overhead • "Works like magic" PerlModule Apache::DBI http://www.modperlcookbook.org/ 165 Speedy DBI • Apache::DBI rocks • Reduces connection overhead • "Works like magic" (action-at-a-distance) PerlModule Apache::DBI http://www.modperlcookbook.org/ 166 Speedy DBI • Apache::DBI rocks • Reduces connection overhead • "Works like magic" (action-at-a-distance) PerlModule Apache::DBI • Speeds up both legacy CGI and mod_perl applications http://www.modperlcookbook.org/ 167 http://www.modperlcookbook.org/ 168 Use what you have http://www.modperlcookbook.org/ 169 Use what you have • Programming using the request cycle represents an entirely different way of looking at your web applications http://www.modperlcookbook.org/ 170 Use what you have • Programming using the request cycle represents an entirely different way of looking at your web applications • Apache provides a rich API for interacting with the request http://www.modperlcookbook.org/ 171 Use what you have • Programming using the request cycle represents an entirely different way of looking at your web applications • Apache provides a rich API for interacting with the request • mod_perl passes the savings to you http://www.modperlcookbook.org/ 172 http://www.modperlcookbook.org/ 173 Objects, smobjects http://www.modperlcookbook.org/ 174 Objects, smobjects • Object-oriented programming has many advantages http://www.modperlcookbook.org/ 175 Objects, smobjects • Object-oriented programming has many advantages – subject of great debate http://www.modperlcookbook.org/ 176 Objects, smobjects • Object-oriented programming has many advantages – subject of great debate • Programming mod_perl in OO makes all kinds of wizardy possible http://www.modperlcookbook.org/ 177 Objects, smobjects • Object-oriented programming has many advantages – subject of great debate • Programming mod_perl in OO makes all kinds of wizardy possible – also makes julienne fries http://www.modperlcookbook.org/ 178 Objects, smobjects • Object-oriented programming has many advantages – subject of great debate • Programming mod_perl in OO makes all kinds of wizardy possible – also makes julienne fries • Let's do more using OOP http://www.modperlcookbook.org/ 179 Perl OO Primer http://www.modperlcookbook.org/ 180 Perl OO Primer • Some basic object-oriented features to understand http://www.modperlcookbook.org/ 181 Perl OO Primer • Some basic object-oriented features to understand – classes – methods – objects – inheritance http://www.modperlcookbook.org/ 182 Pay Homage http://www.modperlcookbook.org/ 183 Pay Homage • The entire object-oriented Perl world owes Damian Conway a huge debt of gratitude http://www.modperlcookbook.org/ 184 Pay Homage • The entire object-oriented Perl world owes Damian Conway a huge debt of gratitude • The definitions that follow are essentially his... http://www.modperlcookbook.org/ 185 Pay Homage • The entire object-oriented Perl world owes Damian Conway a huge debt of gratitude • The definitions that follow are essentially his... • Any mistakes are unquestionably mine http://www.modperlcookbook.org/ 186 Perl Classes http://www.modperlcookbook.org/ 187 Perl Classes • "To create a class, build a package" http://www.modperlcookbook.org/ 188 Perl Classes • "To create a class, build a package" • Perl packages associate variables and subroutines together under a common namespace http://www.modperlcookbook.org/ 189 Perl Classes • "To create a class, build a package" • Perl packages associate variables and subroutines together under a common namespace package My::Dinghy; use 5.006; use strict; 1; http://www.modperlcookbook.org/ 190 Perl Methods http://www.modperlcookbook.org/ 191 Perl Methods • "To create a method, build a subroutine" http://www.modperlcookbook.org/ 192 Perl Methods • "To create a method, build a subroutine" • Perl subroutines can be called as functional subroutines http://www.modperlcookbook.org/ 193 Perl Methods • "To create a method, build a subroutine" • Perl subroutines can be called as functional subroutines print $fh 'print() is a function'; http://www.modperlcookbook.org/ 194 Perl Methods • "To create a method, build a subroutine" • Perl subroutines can be called as functional subroutines print $fh 'print() is a function'; • or as methods http://www.modperlcookbook.org/ 195 Perl Methods • "To create a method, build a subroutine" • Perl subroutines can be called as functional subroutines print $fh 'print() is a function'; • or as methods $fh->print('print() is a method'); http://www.modperlcookbook.org/ 196 Perl Methods • "To create a method, build a subroutine" • Perl subroutines can be called as functional subroutines print $fh 'print() is a function'; • or as methods $fh->print('print() http://www.modperlcookbook.org/ is a method'); 197 Perl Methods • "To create a method, build a subroutine" • Perl subroutines can be called as functional subroutines print $fh 'print() is a function'; • or as methods $fh->print('print() is a method'); sub print { my ($self, @data) = @_; } http://www.modperlcookbook.org/ 198 Perl Methods • "To create a method, build a subroutine" • Perl subroutines can be called as functional subroutines print $fh 'print() is a function'; • or as methods $fh->print('print() is a method'); sub print { my ($self, @data) = @_; } http://www.modperlcookbook.org/ 199 Perl Objects http://www.modperlcookbook.org/ 200 Perl Objects • "To create an object, bless a referent" http://www.modperlcookbook.org/ 201 Perl Objects • "To create an object, bless a referent" • Perl has a special function bless() that associates a variable with a class http://www.modperlcookbook.org/ 202 Perl Objects • "To create an object, bless a referent" • Perl has a special function bless() that associates a variable with a class my $self = {}; return bless $self, $class; http://www.modperlcookbook.org/ 203 Perl Objects • "To create an object, bless a referent" • Perl has a special function bless() that associates a variable with a class my $self = {}; return bless $self, $class; • It's the variable that is associated with the class, not the reference to the variable http://www.modperlcookbook.org/ 204 Perl Objects • "To create an object, bless a referent" • Perl has a special function bless() that associates a variable with a class my $self = {}; return bless $self, $class; • It's the variable that is associated with the class, not the reference to the variable http://www.modperlcookbook.org/ 205 Perl Inhertiance http://www.modperlcookbook.org/ 206 Perl Inhertiance • To create a subclass, populate @ISA http://www.modperlcookbook.org/ 207 Perl Inheritance • To create a subclass, populate @ISA – I made that up for consistency http://www.modperlcookbook.org/ 208 Perl Inheritance • To create a subclass, populate @ISA – I made that up for consistency • @ISA controls how Perl searches for methods when it can't find any in the subclass http://www.modperlcookbook.org/ 209 Perl Inheritance • To create a subclass, populate @ISA – I made that up for consistency • @ISA controls how Perl searches for methods when it can't find any in the subclass • @ISA is a package global http://www.modperlcookbook.org/ 210 Perl Inheritance • To create a subclass, populate @ISA – I made that up for consistency • @ISA controls how Perl searches for methods when it can't find any in the subclass • @ISA is a package global our @ISA = qw(My::Dinghy); http://www.modperlcookbook.org/ 211 Perl Inheritance • To create a subclass, populate @ISA – I made that up for consistency • @ISA controls how Perl searches for methods when it can't find any in the subclass • @ISA is a package global our @ISA = qw(My::Dinghy); use vars qw(@ISA); @ISA = qw(My::Dinghy); http://www.modperlcookbook.org/ 212 Perl Inheritance • To create a subclass, populate @ISA – I made that up for consistency • @ISA controls how Perl searches for methods when it can't find any in the subclass • @ISA is a package global our @ISA = qw(My::Dinghy); use vars qw(@ISA); @ISA = qw(My::Dinghy); @My::12Meter::ISA = qw(My::Dinghy); http://www.modperlcookbook.org/ 213 Guess What? http://www.modperlcookbook.org/ 214 Guess What? • mod_perl has already introduced you to most of Perl's OO semantics http://www.modperlcookbook.org/ 215 Guess What? • mod_perl has already introduced you to most of Perl's OO semantics my $r = Apache->request; http://www.modperlcookbook.org/ 216 Guess What? • mod_perl has already introduced you to most of Perl's OO semantics my $r = Apache->request; my $host = $r->headers_in->get('Host'); http://www.modperlcookbook.org/ 217 Guess What? • mod_perl has already introduced you to most of Perl's OO semantics my $r = Apache->request; my $host = $r->headers_in->get('Host'); • In fact, mod_perl almost begs you to use OO http://www.modperlcookbook.org/ 218 Handlers as Classes http://www.modperlcookbook.org/ 219 Handlers as Classes package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 220 Handlers as Classes package Cookbook::TrapNoHost; use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = shift; unless ($r->headers_in->get('Host') || $r->parsed_uri->hostname) { $r->custom_response(BAD_REQUEST, "Oops! Did you mean to omit a Host header?\n"); return BAD_REQUEST; } return DECLINED; } 1; http://www.modperlcookbook.org/ 221 OO mod_perl • Programming using the mod_perl API forces us to use most of Perls OO tools already http://www.modperlcookbook.org/ 222 OO mod_perl • Programming using the mod_perl API forces us to use most of Perls OO tools already • We just need to fill in a few of the gaps for phenomenal cosmic power... http://www.modperlcookbook.org/ 223 Step #1 http://www.modperlcookbook.org/ 224 Step #1 • Change our existing handlers to method handlers http://www.modperlcookbook.org/ 225 Step #1 • Change our existing handlers to method handlers • Method handlers are just normal handlers called using OO syntax http://www.modperlcookbook.org/ 226 Step #1 • Change our existing handlers to method handlers • Method handlers are just normal handlers called using OO syntax • Allow us to use OO techniques to our advantage http://www.modperlcookbook.org/ 227 Prototyping • The classical way is to use Perl prototypes http://www.modperlcookbook.org/ 228 Prototyping • The classical way is to use Perl prototypes sub handler ($$) { ... } http://www.modperlcookbook.org/ 229 Prototyping • The classical way is to use Perl prototypes sub handler ($$) { ... } • Prototypes are deprecated in 2.0 http://www.modperlcookbook.org/ 230 Attributes • The new way is to use subroutine attributes http://www.modperlcookbook.org/ 231 Attributes • The new way is to use subroutine attributes sub handler : method { ... } http://www.modperlcookbook.org/ 232 Attributes • The new way is to use subroutine attributes sub handler : method { ... } • See the attributes manpage http://www.modperlcookbook.org/ 233 Step #2 http://www.modperlcookbook.org/ 234 Step #2 • Change our handler() method to be able to receive an OO call http://www.modperlcookbook.org/ 235 Step #2 • Change our handler() method to be able to receive an OO call sub handler { my $r = shift; } http://www.modperlcookbook.org/ 236 Step #2 • Change our handler() method to be able to receive an OO call sub handler : method { my ($self, $r) = @_; } http://www.modperlcookbook.org/ 237 Step #2 • Change our handler() method to be able to receive an OO call sub handler : method { my ($self, $r) = @_; } • $self is the invoking class http://www.modperlcookbook.org/ 238 Step #2 • Change our handler() method to be able to receive an OO call sub handler : method { my ($self, $r) = @_; } • $self is the invoking class – most of the time http://www.modperlcookbook.org/ 239 Step #2 • Change our handler() method to be able to receive an OO call sub handler : method { my ($self, $r) = @_; } • $self is the invoking class – most of the time • $r is the same old Apache request object http://www.modperlcookbook.org/ 240 Step #3 http://www.modperlcookbook.org/ 241 Step #3 • Call the handler using a method syntax http://www.modperlcookbook.org/ 242 Step #3 • Call the handler using a method syntax PerlModule My::MethodHandler http://www.modperlcookbook.org/ 243 Step #3 • Call the handler using a method syntax PerlModule My::MethodHandler PerlInitHandler My::MethodHandler->handler http://www.modperlcookbook.org/ 244 Step #3 • Call the handler using a method syntax PerlModule My::MethodHandler PerlInitHandler My::MethodHandler->handler http://www.modperlcookbook.org/ 245 Step #3 • Call the handler using a method syntax PerlModule My::MethodHandler PerlInitHandler My::MethodHandler->handler • Pre-loading is required http://www.modperlcookbook.org/ 246 Step #3 • Call the handler using a method syntax PerlModule My::MethodHandler PerlInitHandler My::MethodHandler->handler • Pre-loading is required • The arrow syntax is not http://www.modperlcookbook.org/ 247 So What? http://www.modperlcookbook.org/ 248 So What? • Normal handlers and method handlers are equivalent in nearly all areas... http://www.modperlcookbook.org/ 249 So What? • Normal handlers and method handlers are equivalent in nearly all areas... • ... but now you have the ability to inherit from other classes using OO techniques http://www.modperlcookbook.org/ 250 For Example http://www.modperlcookbook.org/ 251 For Example • Apache::SSI provides a Perl implementation of Server Side Includes http://www.modperlcookbook.org/ 252 For Example • Apache::SSI provides a Perl implementation of Server Side Includes <Files *.shtml> SetHandler perl-script PerlHandler Apache::SSI </Files> http://www.modperlcookbook.org/ 253 For Example • Apache::SSI provides a Perl implementation of Server Side Includes <Files *.shtml> SetHandler perl-script PerlHandler Apache::SSI </Files> • Equivalent to mod_include except it adds a few important features... http://www.modperlcookbook.org/ 254 Apache::SSI • Integrates with Apache::Filter to provide filtered content generation http://www.modperlcookbook.org/ 255 Apache::SSI • Integrates with Apache::Filter to provide filtered content generation <Location /pipeline> SetHandler perl-script PerlHandler My::Content Apache::SSI Apache::Clean PerlSetVar Filter On </Location> http://www.modperlcookbook.org/ 256 Apache::SSI • Integrates with Apache::Filter to provide filtered content generation <Location /pipeline> SetHandler perl-script PerlHandler My::Content Apache::SSI Apache::Clean PerlSetVar Filter On </Location> http://www.modperlcookbook.org/ 257 Apache::SSI • Integrates with Apache::Filter to provide filtered content generation <Location /pipeline> SetHandler perl-script PerlHandler My::Content Apache::SSI Apache::Clean PerlSetVar Filter On </Location> http://www.modperlcookbook.org/ 258 Apache::SSI • Integrates with Apache::Filter to provide filtered content generation <Location /pipeline> SetHandler perl-script PerlHandler My::Content Apache::SSI Apache::Clean PerlSetVar Filter On </Location> http://www.modperlcookbook.org/ 259 Apache::SSI • Integrates with Apache::Filter to provide filtered content generation <Location /pipeline> SetHandler perl-script PerlHandler My::Content Apache::SSI Apache::Clean PerlSetVar Filter On </Location> • Pipelining like this is impossible using mod_cgi and mod_include http://www.modperlcookbook.org/ 260 Apache::SSI • Integrates with Apache::Filter to provide filtered content generation <Location /pipeline> SetHandler perl-script PerlHandler My::Content Apache::SSI Apache::Clean PerlSetVar Filter On </Location> • Pipelining like this is impossible using mod_cgi and mod_include – in Apache 1.3 at least http://www.modperlcookbook.org/ 261 Drawbacks http://www.modperlcookbook.org/ 262 Drawbacks • Apache::SSI is a huge win for people who like to modularize processing http://www.modperlcookbook.org/ 263 Drawbacks • Apache::SSI is a huge win for people who like to modularize processing • There is one rather limiting drawback to the current implementation http://www.modperlcookbook.org/ 264 Drawbacks • Apache::SSI is a huge win for people who like to modularize processing • There is one rather limiting drawback to the current implementation • If you use the exec or include SSI tag Apache::SSI must be the final filter in the chain http://www.modperlcookbook.org/ 265 Drawbacks • Apache::SSI is a huge win for people who like to modularize processing • There is one rather limiting drawback to the current implementation • If you use the exec or include SSI tag Apache::SSI must be the final filter in the chain PerlHandler My::Content Apache::SSI Apache::Clean http://www.modperlcookbook.org/ 266 Drawbacks • Apache::SSI is a huge win for people who like to modularize processing • There is one rather limiting drawback to the current implementation • If you use the exec or include SSI tag Apache::SSI must be the final filter in the chain PerlHandler My::Content Apache::SSI http://www.modperlcookbook.org/ 267 Drawbacks • Apache::SSI is a huge win for people who like to modularize processing • There is one rather limiting drawback to the current implementation • If you use the exec or include SSI tag Apache::SSI must be the final filter in the chain PerlHandler My::Content Apache::SSI – due to implementation constraints http://www.modperlcookbook.org/ 268 There is Hope http://www.modperlcookbook.org/ 269 There is Hope • Fortunately, Apache::SSI is implemented using method handlers http://www.modperlcookbook.org/ 270 There is Hope • Fortunately, Apache::SSI is implemented using method handlers • We can subclass Apache::SSI and provide our own exec and include implementations that fix the problem http://www.modperlcookbook.org/ 271 There is Hope • Fortunately, Apache::SSI is implemented using method handlers • We can subclass Apache::SSI and provide our own exec and include implementations that fix the problem • We leave all the document parsing and other tag implementations alone http://www.modperlcookbook.org/ 272 There is Hope • Fortunately, Apache::SSI is implemented using method handlers • We can subclass Apache::SSI and provide our own exec and include implementations that fix the problem • We leave all the document parsing and other tag implementations alone * Apache::SSI now includes Apache::FakeSSI which accomplishes the same thing, but is a complete implementation http://www.modperlcookbook.org/ 273 package Cookbook::SSI; use use use use Apache::SSI; HTTP::Request; LWP::UserAgent; strict; @Cookbook::SSI::ISA = qw(Apache::SSI); sub ssi_include { my ($self, $args) = @_; return $self->error("Include must be of type 'virtual'") unless $args->{virtual}; my $uri = Apache::URI->parse(Apache->request); if ($args->{virtual} =~ m!^/!) { $uri->path($args->{virtual}); } else { my ($base) = $uri->path =~ m!(.*/)!; # path is absolute # path is relative $uri->path($base . $args->{virtual}); } my $request = HTTP::Request->new(GET => $uri->unparse); my $response = LWP::UserAgent->new->request($request); return $self->error("Could not Include virtual URL"); unless $response->is_success; return $response->content; } 1; http://www.modperlcookbook.org/ 274 Setup • Just use our module wherever we used to use Apache::SSI http://www.modperlcookbook.org/ 275 Setup • Just use our module wherever we used to use Apache::SSI PerlModule Apache::SSI <Location /pipeline> SetHandler perl-script PerlHandler My::Content Apache::SSI Apache::Clean PerlSetVar Filter On </Location> http://www.modperlcookbook.org/ 276 Setup • Just use our module wherever we used to use Apache::SSI PerlModule Cookbook::SSI <Location /pipeline> SetHandler perl-script PerlHandler My::Content Cookbook::SSI Apache::Clean PerlSetVar Filter On </Location> http://www.modperlcookbook.org/ 277 http://www.modperlcookbook.org/ 278 http://www.modperlcookbook.org/ 279 http://www.modperlcookbook.org/ 280 But wait, there's more... http://www.modperlcookbook.org/ 281 But wait, there's more... • Method handlers are a nice thing to have http://www.modperlcookbook.org/ 282 But wait, there's more... • Method handlers are a nice thing to have • Not very interesting in themselves http://www.modperlcookbook.org/ 283 But wait, there's more... • Method handlers are a nice thing to have • Not very interesting in themselves • Overriding core mod_perl classes is where the real fun begins http://www.modperlcookbook.org/ 284 The Apache Class http://www.modperlcookbook.org/ 285 The Apache Class • The Apache class is at the heart of all we do in mod_perl http://www.modperlcookbook.org/ 286 The Apache Class • The Apache class is at the heart of all we do in mod_perl • Implements most of the amazing things we associate with the mod_perl API http://www.modperlcookbook.org/ 287 The Apache Class • The Apache class is at the heart of all we do in mod_perl • Implements most of the amazing things we associate with the mod_perl API • You can make mod_perl do your own evil bidding by extending and overriding Apache http://www.modperlcookbook.org/ 288 Subclassing Apache http://www.modperlcookbook.org/ 289 Subclassing Apache • Let's make $r->bytes_sent() return KB instead of bytes http://www.modperlcookbook.org/ 290 Subclassing Apache • Let's make $r->bytes_sent() return KB instead of bytes • How? Create a simple subclass that does the calculation for us http://www.modperlcookbook.org/ 291 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 292 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 293 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 294 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 295 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 296 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 297 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 298 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 299 package Cookbook::Apache; use Apache; use strict; @Cookbook::Apache::ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub bytes_sent { return sprintf("%.0f", shift->SUPER::bytes_sent / 1024); } 1; http://www.modperlcookbook.org/ 300 H'wa? http://www.modperlcookbook.org/ 301 H'wa? • What's going on here? our @ISA = qw(Apache); return bless { r => $r }, $class; http://www.modperlcookbook.org/ 302 H'wa? • What's going on here? our @ISA = qw(Apache); return bless { r => $r }, $class; – ask Doug if you see him http://www.modperlcookbook.org/ 303 H'wa? • What's going on here? our @ISA = qw(Apache); return bless { r => $r }, $class; – ask Doug if you see him – typemap r = sv2request_rec($arg, \"$ntype\", cv) – sv2request_rec checks %$arg for _r or r keys calls sv2request_rec using _r or r for $arg http://www.modperlcookbook.org/ 304 H'wa? • What's going on here? our @ISA = qw(Apache); return bless { r => $r }, $class; – ask Doug if you see him – typemap r = sv2request_rec($arg, \"$ntype\", cv) – sv2request_rec checks %$arg for _r or r keys calls sv2request_rec using _r or r for $arg • Hey, it works http://www.modperlcookbook.org/ 305 Sample Usage http://www.modperlcookbook.org/ 306 Sample Usage package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 307 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 308 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 309 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 310 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 311 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 312 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 313 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 314 package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 315 [Wed Jun 12 21:19:35 2002] [error] 9 KB sent for /manual/index.html [Wed Jun 12 21:19:35 2002] [error] 9268 bytes sent for /manual/index.html package My::Bytes; use Apache::Constants qw(OK); use Cookbook::Apache; use strict; sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); $c->log_error($c->bytes_sent, ' KB sent for ', $c->uri); $r->log_error($r->bytes_sent, ' bytes sent for ', $r->uri); return OK; } 1; http://www.modperlcookbook.org/ 316 Let's Simplify http://www.modperlcookbook.org/ 317 Let's Simplify • We only used both $r and $c in this example to show the difference http://www.modperlcookbook.org/ 318 Let's Simplify • We only used both $r and $c in this example to show the difference • Most of the time, you only need one request object, since your subclass inherits all the normal Apache methods http://www.modperlcookbook.org/ 319 Let's Simplify • We only used both $r and $c in this example to show the difference • Most of the time, you only need one request object, since your subclass inherits all the normal Apache methods sub handler { my $r = shift; my $c = Cookbook::Apache->new($r); http://www.modperlcookbook.org/ 320 Let's Simplify • We only used both $r and $c in this example to show the difference • Most of the time, you only need one request object, since your subclass inherits all the normal Apache methods sub handler { my $r = Cookbook::Apache->new(shift); http://www.modperlcookbook.org/ 321 Kick it up a notch http://www.modperlcookbook.org/ 322 Kick it up a notch • Our sample Apache subclass isn't terribly interesting or terribly useful http://www.modperlcookbook.org/ 323 Kick it up a notch • Our sample Apache subclass isn't terribly interesting or terribly useful • Time to add a little heat http://www.modperlcookbook.org/ 324 Cross-Site Scripting http://www.modperlcookbook.org/ 325 Cross-Site Scripting • As web developers, we should always check end-user input before using it http://www.modperlcookbook.org/ 326 Cross-Site Scripting • As web developers, we should always check end-user input before using it • For system() calls, that means making sure no input is tainted http://www.modperlcookbook.org/ 327 Cross-Site Scripting • As web developers, we should always check end-user input before using it • For system() calls, that means making sure no input is tainted – running with -T switch http://www.modperlcookbook.org/ 328 Cross-Site Scripting • As web developers, we should always check end-user input before using it • For system() calls, that means making sure no input is tainted – running with -T switch – PerlTaintCheck On http://www.modperlcookbook.org/ 329 Cross-Site Scripting • As web developers, we should always check end-user input before using it • For system() calls, that means making sure no input is tainted – running with -T switch – PerlTaintCheck On • For HTML output, that means escaping input (< to <) before displaying http://www.modperlcookbook.org/ 330 Cross-Site Scripting • As web developers, we should always check end-user input before using it • For system() calls, that means making sure no input is tainted – running with -T switch – PerlTaintCheck On • For HTML output, that means escaping input (< to <) before displaying – HTML::Entities::escape() http://www.modperlcookbook.org/ 331 Cross-Site Scripting • As web developers, we should always check end-user input before using it • For system() calls, that means making sure no input is tainted – running with -T switch – PerlTaintCheck On • For HTML output, that means escaping input (< to <) before displaying – HTML::Entities::escape() – Apache::Util::escape_html() http://www.modperlcookbook.org/ 332 Lots of Overhead http://www.modperlcookbook.org/ 333 Lots of Overhead • For CGI or mod_perl developers, that means remembering lots of calls to encode() or escape_html() http://www.modperlcookbook.org/ 334 Lots of Overhead • For CGI or mod_perl developers, that means remembering lots of calls to encode() or escape_html() – which, of course, everyone does. Right? http://www.modperlcookbook.org/ 335 Lots of Overhead • For CGI or mod_perl developers, that means remembering lots of calls to encode() or escape_html() – which, of course, everyone does. Right? • By subclassing the Apache class we can escape output automatically http://www.modperlcookbook.org/ 336 Lots of Overhead • For CGI or mod_perl developers, that means remembering lots of calls to encode() or escape_html() – which, of course, everyone does. Right? • By subclassing the Apache class we can escape output automatically • We don't want to escape all output, just the stuff from the end-user http://www.modperlcookbook.org/ 337 Lots of Overhead • For CGI or mod_perl developers, that means remembering lots of calls to encode() or escape_html() – which, of course, everyone does. Right? • By subclassing the Apache class we can escape output automatically • We don't want to escape all output, just the stuff from the end-user • Get help from Taint.pm http://www.modperlcookbook.org/ 338 Background http://www.modperlcookbook.org/ 339 Background • We need to add PerlTaintCheck On to our httpd.conf to mimic the -T switch http://www.modperlcookbook.org/ 340 Background • We need to add PerlTaintCheck On to our httpd.conf to mimic the -T switch • Use Taint::tainted() to determine whether data is tainted http://www.modperlcookbook.org/ 341 Background • We need to add PerlTaintCheck On to our httpd.conf to mimic the -T switch • Use Taint::tainted() to determine whether data is tainted not eval { join("",@_), kill 0; 1 }; http://www.modperlcookbook.org/ 342 Background • We need to add PerlTaintCheck On to our httpd.conf to mimic the -T switch • Use Taint::tainted() to determine whether data is tainted not eval { join("",@_), kill 0; 1 }; • "send the data along" http://www.modperlcookbook.org/ 343 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 344 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 345 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 346 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 347 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 348 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 349 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 350 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 351 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 352 package Cookbook::TaintRequest; use Apache; use Apache::Util qw(escape_html); # Module load will die if PerlTaintCheck Off use Taint qw(tainted); use strict; @Cookbook::TaintRequest::ISA = qw(Apache); sub print { my ($self, @data) = @_; foreach my $value (@data) { # Dereference scalar references. $value = $$value if ref $value eq 'SCALAR'; # Escape any HTML content if the data is tainted. $value = escape_html($value) if tainted($value); } $self->SUPER::print(@data); } http://www.modperlcookbook.org/ 353 What's missing? http://www.modperlcookbook.org/ 354 What's missing? • So far all we have done is override mod_perl's $r->print() http://www.modperlcookbook.org/ 355 What's missing? • So far all we have done is override mod_perl's $r->print() • We still have to create a constructor for our class http://www.modperlcookbook.org/ 356 What's missing? • So far all we have done is override mod_perl's $r->print() • We still have to create a constructor for our class • But what about just plain print()? print "<script>Heh!</script>"; http://www.modperlcookbook.org/ 357 What's missing? • So far all we have done is override mod_perl's $r->print() • We still have to create a constructor for our class • But what about just plain print()? print "<script>Heh!</script>"; • Our constructor needs to be special http://www.modperlcookbook.org/ 358 TIEHANDLE • Perl provides the TIEHANDLE interface as a way to override how filehandles behave when written to or read from http://www.modperlcookbook.org/ 359 TIEHANDLE • Perl provides the TIEHANDLE interface as a way to override how filehandles behave when written to or read from print "foo"; http://www.modperlcookbook.org/ 360 TIEHANDLE • Perl provides the TIEHANDLE interface as a way to override how filehandles behave when written to or read from print "foo"; print STDOUT "foo"; http://www.modperlcookbook.org/ 361 TIEHANDLE • Perl provides the TIEHANDLE interface as a way to override how filehandles behave when written to or read from print "foo"; print STDOUT "foo"; • mod_perl uses tied filehandles to our advantage http://www.modperlcookbook.org/ 362 More mod_perl Magic http://www.modperlcookbook.org/ 363 More mod_perl Magic • $r->print() sends data to the client http://www.modperlcookbook.org/ 364 More mod_perl Magic • $r->print() sends data to the client – wrapper around Apache API calls that write data over the wire http://www.modperlcookbook.org/ 365 More mod_perl Magic • $r->print() sends data to the client – wrapper around Apache API calls that write data over the wire • mod_perl ties standard streams to the Apache class http://www.modperlcookbook.org/ 366 More mod_perl Magic • $r->print() sends data to the client – wrapper around Apache API calls that write data over the wire • mod_perl ties standard streams to the Apache class – writes to STDOUT use $r->print() http://www.modperlcookbook.org/ 367 More mod_perl Magic • $r->print() sends data to the client – wrapper around Apache API calls that write data over the wire • mod_perl ties standard streams to the Apache class – writes to STDOUT use $r->print() • why Apache::Registry works http://www.modperlcookbook.org/ 368 More mod_perl Magic • $r->print() sends data to the client – wrapper around Apache API calls that write data over the wire • mod_perl ties standard streams to the Apache class – writes to STDOUT use $r->print() • why Apache::Registry works – reads from STDIN use Apache API calls to get data from the wire http://www.modperlcookbook.org/ 369 More mod_perl Magic • $r->print() sends data to the client – wrapper around Apache API calls that write data over the wire • mod_perl ties standard streams to the Apache class – writes to STDOUT use $r->print() • why Apache::Registry works – reads from STDIN use Apache API calls to get data from the wire • We need to intercept both $r->print() and print()s to STDOUT http://www.modperlcookbook.org/ 370 Apache's TIEHANDLE http://www.modperlcookbook.org/ 371 Apache's TIEHANDLE • The Apache class provides a TIEHANDLE interface http://www.modperlcookbook.org/ 372 Apache's TIEHANDLE • The Apache class provides a TIEHANDLE interface • When subclassing the Apache class, we can override the PRINT part of the interface http://www.modperlcookbook.org/ 373 Apache's TIEHANDLE • The Apache class provides a TIEHANDLE interface • When subclassing the Apache class, we can override the PRINT part of the interface • Leave the other TIEHANDLE parts in tact http://www.modperlcookbook.org/ 374 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } http://www.modperlcookbook.org/ 375 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } http://www.modperlcookbook.org/ 376 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } http://www.modperlcookbook.org/ 377 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } http://www.modperlcookbook.org/ 378 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; } sub PRINT { shift->print(@_); } 1; http://www.modperlcookbook.org/ 379 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; } sub PRINT { shift->print(@_); } 1; http://www.modperlcookbook.org/ 380 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; } sub PRINT { shift->print(@_); } 1; http://www.modperlcookbook.org/ 381 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; } sub PRINT { shift->print(@_); } 1; http://www.modperlcookbook.org/ 382 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; } sub PRINT { shift->print(@_); } 1; http://www.modperlcookbook.org/ 383 sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; } sub PRINT { shift->print(@_); } 1; http://www.modperlcookbook.org/ 384 Let's Try It Out http://www.modperlcookbook.org/ 385 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 386 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 387 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 388 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 389 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 390 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 391 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 392 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); $r->print("<html>You entered ", @data, "<br/></html>"); return OK; } 1; http://www.modperlcookbook.org/ 393 package Cookbook::TaintTest; use Apache::Constants qw(OK); use Cookbook::TaintRequest; use strict; sub handler { my $r = Cookbook::TaintRequest->new(shift); my @data = $r->args; # Untaint input data if magic word "override" is present. $data[1] =~ m/(.*override.*)/; $data[1] = $1 if $1; $r->send_http_header('text/html'); print "<html>You entered ", @data, "<br/></html>"; return OK; } 1; http://www.modperlcookbook.org/ 394 /tainted?x=<script>alert("Hi!")</script> http://www.modperlcookbook.org/ 395 /tainted?x=<script>alert("Hi!")</script> http://www.modperlcookbook.org/ 396 /tainted?x=<script>alert("Hi!")</script> http://www.modperlcookbook.org/ 397 /tainted?x=<script>alert("override Hi!")</script> http://www.modperlcookbook.org/ 398 /tainted?x=<script>alert("override Hi!")</script> http://www.modperlcookbook.org/ 399 /tainted?x=<script>alert("override Hi!")</script> http://www.modperlcookbook.org/ 400 http://www.modperlcookbook.org/ 401 Registry has issues... http://www.modperlcookbook.org/ 402 Registry has issues... • Apache::Registry is incredibly cool http://www.modperlcookbook.org/ 403 Registry has issues... • Apache::Registry is incredibly cool • But it's not perfect http://www.modperlcookbook.org/ 404 Registry has issues... • Apache::Registry is incredibly cool • But it's not perfect Apache::Registry - Run unaltered CGI scrips under mod_perl http://www.modperlcookbook.org/ 405 Registry has issues... • Apache::Registry is incredibly cool • But it's not perfect Apache::Registry - Run unaltered CGI scrips under mod_perl • Scripts that work perfectly well under mod_cgi balk with Apache::Registry http://www.modperlcookbook.org/ 406 Registry has issues... • Apache::Registry is incredibly cool • But it's not perfect Apache::Registry - Run unaltered CGI scrips under mod_perl • Scripts that work perfectly well under mod_cgi balk with Apache::Registry – that's why there's Apache::PerlRun http://www.modperlcookbook.org/ 407 Registry has issues... • Apache::Registry is incredibly cool • But it's not perfect Apache::Registry - Run unaltered CGI scrips under mod_perl • Scripts that work perfectly well under mod_cgi balk with Apache::Registry – that's why there's Apache::PerlRun • Not even PerlRun can save you from some of the issues http://www.modperlcookbook.org/ 408 For instance... http://www.modperlcookbook.org/ 409 For instance... • Mixing mod_perl with legacy CGI code can be difficult http://www.modperlcookbook.org/ 410 For instance... • Mixing mod_perl with legacy CGI code can be difficult – now you can isolate processing in phases other than content-generation http://www.modperlcookbook.org/ 411 For instance... • Mixing mod_perl with legacy CGI code can be difficult – now you can isolate processing in phases other than content-generation • What happens when you need POST data in other phases and legacy Apache::Registry code? http://www.modperlcookbook.org/ 412 For instance... • Mixing mod_perl with legacy CGI code can be difficult – now you can isolate processing in phases other than content-generation • What happens when you need POST data in other phases and legacy Apache::Registry code? • What we need is a way to cache POST data that makes it accessible to legacy CGI scripts http://www.modperlcookbook.org/ 413 Registry: The Next Generation http://www.modperlcookbook.org/ 414 Registry: The Next Generation http://www.modperlcookbook.org/ 415 Registry: The Next Generation • Apache::Registry is pretty complex and difficult to alter http://www.modperlcookbook.org/ 416 Registry: The Next Generation • Apache::Registry is pretty complex and difficult to alter • Apache::RegistryNG behaves almost the same, but is object-oriented and a much better candidate for subclassing http://www.modperlcookbook.org/ 417 Registry: The Next Generation • Apache::Registry is pretty complex and difficult to alter • Apache::RegistryNG behaves almost the same, but is object-oriented and a much better candidate for subclassing • Apache::RegistryNG is actually a subclass of Apache::PerlRun http://www.modperlcookbook.org/ 418 Do more! • Let's do more by subclassing Apache::RegistryNG http://www.modperlcookbook.org/ 419 Do more! • Let's do more by subclassing Apache::RegistryNG • tie STDIN instead... http://www.modperlcookbook.org/ 420 package Apache::CachePOSTRegistry; use Apache::RegistryNG; use Apache::Request; use strict; @Apache::CachePOSTRegistry::ISA = qw(Apache::RegistryNG); sub new { my ($class, $r) = @_; $r = Apache::Request->instance($r || Apache->request); tie *STDIN, $class, $r; return tied *STDIN; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; }http://www.modperlcookbook.org/ 421 package Apache::CachePOSTRegistry; use Apache::RegistryNG; use Apache::Request; use strict; @Apache::CachePOSTRegistry::ISA = qw(Apache::RegistryNG); sub new { my ($class, $r) = @_; $r = Apache::Request->instance($r || Apache->request); tie *STDIN, $class, $r; return tied *STDIN; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; }http://www.modperlcookbook.org/ 422 package Apache::CachePOSTRegistry; use Apache::RegistryNG; use Apache::Request; use strict; @Apache::CachePOSTRegistry::ISA = qw(Apache::RegistryNG); sub new { my ($class, $r) = @_; $r = Apache::Request->instance($r || Apache->request); tie *STDIN, $class, $r; return tied *STDIN; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; }http://www.modperlcookbook.org/ 423 package Apache::CachePOSTRegistry; use Apache::RegistryNG; use Apache::Request; use strict; @Apache::CachePOSTRegistry::ISA = qw(Apache::RegistryNG); sub new { my ($class, $r) = @_; $r = Apache::Request->instance($r || Apache->request); tie *STDIN, $class, $r; return tied *STDIN; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; }http://www.modperlcookbook.org/ 424 package Apache::CachePOSTRegistry; use Apache::RegistryNG; use Apache::Request; use strict; @Apache::CachePOSTRegistry::ISA = qw(Apache::RegistryNG); sub new { my ($class, $r) = @_; $r = Apache::Request->instance($r || Apache->request); tie *STDIN, $class, $r; return tied *STDIN; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; }http://www.modperlcookbook.org/ 425 package Apache::CachePOSTRegistry; use Apache::RegistryNG; use Apache::Request; use strict; @Apache::CachePOSTRegistry::ISA = qw(Apache::RegistryNG); sub new { my ($class, $r) = @_; $r = Apache::Request->instance($r || Apache->request); tie *STDIN, $class, $r; return tied *STDIN; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; }http://www.modperlcookbook.org/ 426 Apache::Request->instance() http://www.modperlcookbook.org/ 427 Apache::Request->instance() • Same as Apache::Request->new() http://www.modperlcookbook.org/ 428 Apache::Request->instance() • Same as Apache::Request->new() • Stores away an Apache::Request object (with the POST data) and uses it for future instantiations http://www.modperlcookbook.org/ 429 Apache::Request->instance() • Same as Apache::Request->new() • Stores away an Apache::Request object (with the POST data) and uses it for future instantiations sub instance { my $class = shift; my $r = shift; if (my $apreq = $r->pnotes('apreq')) { return $apreq; } my $new_req = $class->new($r, @_); $r->pnotes('apreq', $new_req); return $new_req; } http://www.modperlcookbook.org/ 430 Apache::Request->instance() • Same as Apache::Request->new() • Stores away an Apache::Request object (with the POST data) and uses it for future instantiations sub instance { my $class = shift; my $r = shift; if (my $apreq = $r->pnotes('apreq')) { return $apreq; } my $new_req = $class->new($r, @_); $r->pnotes('apreq', $new_req); return $new_req; } http://www.modperlcookbook.org/ 431 Apache::Request->instance() • Same as Apache::Request->new() • Stores away an Apache::Request object (with the POST data) and uses it for future instantiations sub instance { my $class = shift; my $r = shift; if (my $apreq = $r->pnotes('apreq')) { return $apreq; } my $new_req = $class->new($r, @_); $r->pnotes('apreq', $new_req); return $new_req; } http://www.modperlcookbook.org/ 432 Apache::Request->instance() • Same as Apache::Request->new() • Stores away an Apache::Request object (with the POST data) and uses it for future instantiations sub instance { my $class = shift; my $r = shift; if (my $apreq = $r->pnotes('apreq')) { return $apreq; } my $new_req = $class->new($r, @_); $r->pnotes('apreq', $new_req); return $new_req; } http://www.modperlcookbook.org/ 433 Apache::Request->instance() • Same as Apache::Request->new() • Stores away an Apache::Request object (with the POST data) and uses it for future instantiations sub instance { my $class = shift; my $r = shift; if (my $apreq = $r->pnotes('apreq')) { return $apreq; } my $new_req = $class->new($r, @_); $r->pnotes('apreq', $new_req); return $new_req; } http://www.modperlcookbook.org/ 434 package Apache::CachePOSTRegistry; use Apache::RegistryNG; use Apache::Request; use strict; @Apache::CachePOSTRegistry::ISA = qw(Apache::RegistryNG); sub new { my ($class, $r) = @_; $r = Apache::Request->instance($r || Apache->request); tie *STDIN, $class, $r; return tied *STDIN; } sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; }http://www.modperlcookbook.org/ 435 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 436 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 437 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 438 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 439 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 440 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 441 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 442 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 443 sub READ { my my my my $self = shift; $buf = \($_[0]); shift; $len = shift; $offset = shift || 0; my @args = (); $self->{r}->param->do(sub { push @args, join '=', @_; 1; }); my $input = join '&', @args; $input =~ s! !+!g; substr($$buf, $offset) = substr($input, 0, $len); substr($input, 0, $len) = ''; return length substr($$buf, $offset); } 1; http://www.modperlcookbook.org/ 444 It Works! http://www.modperlcookbook.org/ 445 It Works! sub My::InitHandler { my $r = Apache::Request->instance(shift); my @post = $r->param; ... return Apache::Constants::OK; } 1; http://www.modperlcookbook.org/ 446 It Works! sub My::InitHandler { my $r = Apache::Request->instance(shift); my @post = $r->param; ... return Apache::Constants::OK; } 1; http://www.modperlcookbook.org/ 447 It Works! sub My::InitHandler { my $r = Apache::Request->instance(shift); my @post = $r->param; ... return Apache::Constants::OK; } 1; http://www.modperlcookbook.org/ 448 It Works! sub My::InitHandler { my $r = Apache::Request->instance(shift); my @post = $r->param; ... return Apache::Constants::OK; } 1; #!/usr/bin/perl read(STDIN, my $posted, $ENV{'CONTENT_LENGTH'}); print "Content-type: text/plain\n\n"; print $posted; http://www.modperlcookbook.org/ 449 It Works! sub My::InitHandler { my $r = Apache::Request->instance(shift); my @post = $r->param; ... return Apache::Constants::OK; } 1; #!/usr/bin/perl use CGI; my $q = CGI->new; print $q->header(-type=>'text/plain'); print $q->param; http://www.modperlcookbook.org/ 450 http://www.modperlcookbook.org/ 451 XS Anyone? http://www.modperlcookbook.org/ 452 XS Anyone? • Just for kicks, let's throw some XS into the mix http://www.modperlcookbook.org/ 453 XS Anyone? • Just for kicks, let's throw some XS into the mix • XS is the glue that ties Apache and Perl together http://www.modperlcookbook.org/ 454 XS Anyone? • Just for kicks, let's throw some XS into the mix • XS is the glue that ties Apache and Perl together – most of the mod_perl API lives in Apache.xs http://www.modperlcookbook.org/ 455 XS Anyone? • Just for kicks, let's throw some XS into the mix • XS is the glue that ties Apache and Perl together – most of the mod_perl API lives in Apache.xs • Unfortunately, mod_perl 1.3 doesn't offer up all the Apache API, only selected parts http://www.modperlcookbook.org/ 456 XS Anyone? • Just for kicks, let's throw some XS into the mix • XS is the glue that ties Apache and Perl together – most of the mod_perl API lives in Apache.xs • Unfortunately, mod_perl 1.3 doesn't offer up all the Apache API, only selected parts • With XS, we can open up the rest... http://www.modperlcookbook.org/ 457 Assbackwards http://www.modperlcookbook.org/ 458 Assbackwards • In Apache-speak, a request that is assbackwards is an HTTP/0.9 request http://www.modperlcookbook.org/ 459 Assbackwards • In Apache-speak, a request that is assbackwards is an HTTP/0.9 request • Apache still supports HTTP/0.9 http://www.modperlcookbook.org/ 460 Assbackwards • In Apache-speak, a request that is assbackwards is an HTTP/0.9 request • Apache still supports HTTP/0.9 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi http://www.modperlcookbook.org/ 461 Assbackwards • In Apache-speak, a request that is assbackward is an HTTP/0.9 request • Apache still supports HTTP/0.9 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi http://www.modperlcookbook.org/ 462 Assbackwards • In Apache-speak, a request that is assbackward is an HTTP/0.9 request • Apache still supports HTTP/0.9 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi Hi http://www.modperlcookbook.org/ 463 The assbackwards Flag http://www.modperlcookbook.org/ 464 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record http://www.modperlcookbook.org/ 465 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record • If r->assbackwards is set, Apache doesn't send any headers http://www.modperlcookbook.org/ 466 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record • If r->assbackwards is set, Apache doesn't send any headers • mod_perl does not provide a way to access the assbackwards flag http://www.modperlcookbook.org/ 467 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record • If r->assbackwards is set, Apache doesn't send any headers • mod_perl does not provide a way to access the assbackwards flag • but it uses it when convenient http://www.modperlcookbook.org/ 468 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record • If r->assbackwards is set, Apache doesn't send any headers • mod_perl does not provide a way to access the assbackwards flag • but it uses it when convenient my $sub = $r->lookup_uri('/layline.html'); http://www.modperlcookbook.org/ 469 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record • If r->assbackwards is set, Apache doesn't send any headers • mod_perl does not provide a way to access the assbackwards flag • but it uses it when convenient my $sub = $r->lookup_uri('/layline.html'); $sub->run(); http://www.modperlcookbook.org/ 470 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record • If r->assbackwards is set, Apache doesn't send any headers • mod_perl does not provide a way to access the assbackwards flag • but it uses it when convenient my $sub = $r->lookup_uri('/layline.html'); $sub->run(1); http://www.modperlcookbook.org/ 471 The assbackwards Flag • Apache marks HTTP/0.9 requests with the assbackwards flag in the request record • If r->assbackwards is set, Apache doesn't send any headers • mod_perl does not provide a way to access the assbackwards flag • but it uses it when convenient my $sub = $r->lookup_uri('/layline.html'); $sub->run(1); • You can too! http://www.modperlcookbook.org/ 472 Approach http://www.modperlcookbook.org/ 473 Approach • Accessing parts of the Apache API that mod_perl does not natively support requires XS http://www.modperlcookbook.org/ 474 Approach • Accessing parts of the Apache API that mod_perl does not natively support requires XS – Assbackwards.pm http://www.modperlcookbook.org/ 475 Approach • Accessing parts of the Apache API that mod_perl does not natively support requires XS – Assbackwards.pm – Assbackwards.xs http://www.modperlcookbook.org/ 476 Approach • Accessing parts of the Apache API that mod_perl does not natively support requires XS – Assbackwards.pm – Assbackwards.xs – typemap http://www.modperlcookbook.org/ 477 Approach • Accessing parts of the Apache API that mod_perl does not natively support requires XS – Assbackwards.pm – Assbackwards.xs – typemap – Makefile.PL http://www.modperlcookbook.org/ 478 Approach • Accessing parts of the Apache API that mod_perl does not natively support requires XS – Assbackwards.pm – Assbackwards.xs – typemap – Makefile.PL • Fortunately, in this case, all the parts are simple http://www.modperlcookbook.org/ 479 The Perl Part http://www.modperlcookbook.org/ 480 package Apache::Assbackwards; use 5.006; use strict; use warnings; use DynaLoader; our @ISA = qw(DynaLoader Apache); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } 1; http://www.modperlcookbook.org/ 481 package Apache::Assbackwards; use 5.006; use strict; use warnings; use DynaLoader; our @ISA = qw(DynaLoader Apache); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } 1; http://www.modperlcookbook.org/ 482 package Apache::Assbackwards; use 5.006; use strict; use warnings; use DynaLoader; our @ISA = qw(DynaLoader Apache); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } 1; http://www.modperlcookbook.org/ 483 package Apache::Assbackwards; use 5.006; use strict; use warnings; use DynaLoader; our @ISA = qw(DynaLoader Apache); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } 1; http://www.modperlcookbook.org/ 484 package Apache::Assbackwards; use 5.006; use strict; use warnings; use DynaLoader; our @ISA = qw(DynaLoader Apache); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } 1; http://www.modperlcookbook.org/ 485 package Apache::Assbackwards; use 5.006; use strict; use warnings; use DynaLoader; our @ISA = qw(DynaLoader Apache); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } 1; http://www.modperlcookbook.org/ 486 package Apache::Assbackwards; use 5.006; use strict; use warnings; use DynaLoader; our @ISA = qw(DynaLoader Apache); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } 1; http://www.modperlcookbook.org/ 487 The XS Part http://www.modperlcookbook.org/ 488 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 489 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 490 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 491 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 492 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 493 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 494 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 495 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 496 #include #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" "mod_perl_xs.h" MODULE = Apache::Assbackwards PACKAGE = Apache::Assbackwards PROTOTYPES: ENABLE int assbackwards(r, ...) Apache r CODE: get_set_IV(r->assbackwards); OUTPUT: RETVAL http://www.modperlcookbook.org/ 497 The typemap Part http://www.modperlcookbook.org/ 498 The typemap Part TYPEMAP Apache T_APACHEOBJ OUTPUT T_APACHEOBJ sv_setref_pv($arg, \"${ntype}\", (void*)$var); INPUT T_APACHEOBJ r = sv2request_rec($arg, \"$ntype\", cv) http://www.modperlcookbook.org/ 499 The Makefile.PL Part http://www.modperlcookbook.org/ 500 The Makefile.PL Part use Apache::src (); WriteMakefile( NAME => 'Apache::Assbackwards', VERSION_FROM => 'Assbackwards.pm', INC => Apache::src->new->inc, } http://www.modperlcookbook.org/ 501 The Makefile.PL Part use Apache::src (); WriteMakefile( NAME => 'Apache::Assbackwards', VERSION_FROM => 'Assbackwards.pm', INC => Apache::src->new->inc, } http://www.modperlcookbook.org/ 502 The Makefile.PL Part use Apache::src (); WriteMakefile( NAME => 'Apache::Assbackwards', VERSION_FROM => 'Assbackwards.pm', INC => Apache::src->new->inc, } http://www.modperlcookbook.org/ 503 The Makefile.PL Part use Apache::src (); WriteMakefile( NAME VERSION_FROM INC TYPEMAPS } => => => => 'Apache::Assbackwards', 'Assbackwards.pm', Apache::src->new->inc, Apache::src->new->typemaps, http://www.modperlcookbook.org/ 504 make http://www.modperlcookbook.org/ 505 make • Just use the canonical $ perl Makefile.PL $ make $ sudo make install and you're good to go http://www.modperlcookbook.org/ 506 How do we use it? http://www.modperlcookbook.org/ 507 How do we use it? package My::Assbackwards; use Apache::Assbackwards; use strict; sub handler { my $r = Apache::Assbackwards->new(shift); $r->assbackwards(1); return Apache::Constants::OK; } 1; http://www.modperlcookbook.org/ 508 How do we use it? package My::Assbackwards; use Apache::Assbackwards; use strict; sub handler { my $r = Apache::Assbackwards->new(shift); $r->assbackwards(1); return Apache::Constants::OK; } 1; http://www.modperlcookbook.org/ 509 How do we use it? package My::Assbackwards; use Apache::Assbackwards; use strict; sub handler { my $r = Apache::Assbackwards->new(shift); $r->assbackwards(1); return Apache::Constants::OK; } 1; http://www.modperlcookbook.org/ 510 How do we use it? package My::Assbackwards; use Apache::Assbackwards; use strict; sub handler { my $r = Apache::Assbackwards->new(shift); $r->assbackwards(1); return Apache::Constants::OK; } 1; http://www.modperlcookbook.org/ 511 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi HTTP/1.0 http://www.modperlcookbook.org/ 512 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi HTTP/1.0 HTTP/1.1 200 OK Date: Sat, 15 Jun 2002 19:08:48 GMT Server: Apache/1.3.25-dev (Unix) mod_perl/1.27_01-dev Perl/v5.8.0 Expires: Sat, 15 Jun 2002 19:08:50 GMT Connection: close Content-Type: text/plain; charset=ISO-8859-1 Hi http://www.modperlcookbook.org/ 513 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi HTTP/1.0 HTTP/1.1 200 OK Date: Sat, 15 Jun 2002 19:08:48 GMT Server: Apache/1.3.25-dev (Unix) mod_perl/1.27_01-dev Perl/v5.8.0 Expires: Sat, 15 Jun 2002 19:08:50 GMT Connection: close Content-Type: text/plain; charset=ISO-8859-1 Hi $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi HTTP/1.0 http://www.modperlcookbook.org/ 514 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi HTTP/1.0 HTTP/1.1 200 OK Date: Sat, 15 Jun 2002 19:08:48 GMT Server: Apache/1.3.25-dev (Unix) mod_perl/1.27_01-dev Perl/v5.8.0 Expires: Sat, 15 Jun 2002 19:08:50 GMT Connection: close Content-Type: text/plain; charset=ISO-8859-1 Hi $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi HTTP/1.0 Hi http://www.modperlcookbook.org/ 515 That was fun, but... • Ok, the assbackwards flag is amusing but not all too practical http://www.modperlcookbook.org/ 516 That was fun, but... • Ok, the assbackwards flag is amusing but not all too practical • How about something more applicable? http://www.modperlcookbook.org/ 517 That was fun, but... • Ok, the assbackwards flag is amusing but not all too practical • How about something more applicable? – ap_note_digest_auth_failure http://www.modperlcookbook.org/ 518 The XS Part #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" MODULE = Apache::AuthDigest::API PACKAGE = Apache::AuthDigest::API PROTOTYPES: ENABLE void note_digest_auth_failure(r) Apache r CODE: ap_note_digest_auth_failure(r); http://www.modperlcookbook.org/ 519 The XS Part #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" MODULE = Apache::AuthDigest::API PACKAGE = Apache::AuthDigest::API PROTOTYPES: ENABLE void note_digest_auth_failure(r) Apache r CODE: ap_note_digest_auth_failure(r); http://www.modperlcookbook.org/ 520 The XS Part #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" MODULE = Apache::AuthDigest::API PACKAGE = Apache::AuthDigest::API PROTOTYPES: ENABLE void note_digest_auth_failure(r) Apache r CODE: ap_note_digest_auth_failure(r); http://www.modperlcookbook.org/ 521 The XS Part #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" MODULE = Apache::AuthDigest::API PACKAGE = Apache::AuthDigest::API PROTOTYPES: ENABLE void note_digest_auth_failure(r) Apache r CODE: ap_note_digest_auth_failure(r); http://www.modperlcookbook.org/ 522 The XS Part #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" MODULE = Apache::AuthDigest::API PACKAGE = Apache::AuthDigest::API PROTOTYPES: ENABLE void note_digest_auth_failure(r) Apache r CODE: ap_note_digest_auth_failure(r); http://www.modperlcookbook.org/ 523 The Perl Part http://www.modperlcookbook.org/ 524 ... The Perl Part our $VERSION = '0.01'; our @ISA = qw(DynaLoader Apache); __PACKAGE__->bootstrap($VERSION); http://www.modperlcookbook.org/ 525 ... The Perl Part our $VERSION = '0.01'; our @ISA = qw(DynaLoader Apache); __PACKAGE__->bootstrap($VERSION); ... sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } http://www.modperlcookbook.org/ 526 The Perl Part ... our $VERSION = '0.01'; our @ISA = qw(DynaLoader Apache); __PACKAGE__->bootstrap($VERSION); ... sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } ... unless ($response) { $log->info("Client did not supply a Digest response"); $r->note_digest_auth_failure; return AUTH_REQUIRED } http://www.modperlcookbook.org/ 527 The Perl Part ... our $VERSION = '0.01'; our @ISA = qw(DynaLoader Apache); __PACKAGE__->bootstrap($VERSION); ... sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } ... unless ($response) { $log->info("Client did not supply a Digest response"); $r->note_digest_auth_failure; return AUTH_REQUIRED } http://www.modperlcookbook.org/ 528 More? http://www.modperlcookbook.org/ 529 More? • Apache provides for something called "registered cleanups" http://www.modperlcookbook.org/ 530 More? • Apache provides for something called "registered cleanups" $r->register_cleanup(\&cleanup); http://www.modperlcookbook.org/ 531 More? • Apache provides for something called "registered cleanups" $r->register_cleanup(\&cleanup); PerlCleanupHandler My::Cleanup http://www.modperlcookbook.org/ 532 More? • Apache provides for something called "registered cleanups" $r->register_cleanup(\&cleanup); PerlCleanupHandler My::Cleanup Apache->server->register_cleanup(\&cleanup); http://www.modperlcookbook.org/ 533 More? • Apache provides for something called "registered cleanups" $r->register_cleanup(\&cleanup); PerlCleanupHandler My::Cleanup Apache->server->register_cleanup(\&cleanup); • Cleanups allow you to schedule processing at various end points in the Apache runtime http://www.modperlcookbook.org/ 534 More? • Apache provides for something called "registered cleanups" $r->register_cleanup(\&cleanup); PerlCleanupHandler My::Cleanup Apache->server->register_cleanup(\&cleanup); • Cleanups allow you to schedule processing at various end points in the Apache runtime • All use Apache's concept of memory pools http://www.modperlcookbook.org/ 535 Keepalives • HTTP/1.1 allows for more than one request per connection http://www.modperlcookbook.org/ 536 Keepalives • HTTP/1.1 allows for more than one request per connection Connection: Keep-Alive http://www.modperlcookbook.org/ 537 Keepalives • HTTP/1.1 allows for more than one request per connection Connection: Keep-Alive http://www.modperlcookbook.org/ 538 Keepalives • HTTP/1.1 allows for more than one request per connection Connection: Keep-Alive • Connections are interfaces with the Apache::Connection class http://www.modperlcookbook.org/ 539 Keepalives • HTTP/1.1 allows for more than one request per connection Connection: Keep-Alive • Connections are interfaces with the Apache::Connection class my $c = $r->connection; http://www.modperlcookbook.org/ 540 Keepalives • HTTP/1.1 allows for more than one request per connection Connection: Keep-Alive • Connections are interfaces with the Apache::Connection class my $c = $r->connection; • Apache creates a per-connection memory pool http://www.modperlcookbook.org/ 541 Keepalives • HTTP/1.1 allows for more than one request per connection Connection: Keep-Alive • Connections are interfaces with the Apache::Connection class my $c = $r->connection; • Apache creates a per-connection memory pool • mod_perl provides no way to register per-connection cleanups http://www.modperlcookbook.org/ 542 Per-connection Cleanups http://www.modperlcookbook.org/ 543 Per-connection Cleanups • Unfortunately, mod_perl wasn't designed so you could subclass the Apache::Connection class http://www.modperlcookbook.org/ 544 Per-connection Cleanups • Unfortunately, mod_perl wasn't designed so you could subclass the Apache::Connection class • Let's subclass the Apache class and re-bless $r->connection into our class http://www.modperlcookbook.org/ 545 package Apache::ConnectionCleanup; use 5.006; use strict; use Apache; use Apache::ConnectionCleanup::RegisterCleanup; our @ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub connection { my $connection = shift->SUPER::connection; return bless $connection, 'Apache::ConnectionCleanup::RegisterCleanup'; } 1; http://www.modperlcookbook.org/ 546 package Apache::ConnectionCleanup; use 5.006; use strict; use Apache; use Apache::ConnectionCleanup::RegisterCleanup; our @ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub connection { my $connection = shift->SUPER::connection; return bless $connection, 'Apache::ConnectionCleanup::RegisterCleanup'; } 1; http://www.modperlcookbook.org/ 547 package Apache::ConnectionCleanup; use 5.006; use strict; use Apache; use Apache::ConnectionCleanup::RegisterCleanup; our @ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub connection { my $connection = shift->SUPER::connection; return bless $connection, 'Apache::ConnectionCleanup::RegisterCleanup'; } 1; http://www.modperlcookbook.org/ 548 package Apache::ConnectionCleanup; use 5.006; use strict; use Apache; use Apache::ConnectionCleanup::RegisterCleanup; our @ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub connection { my $connection = shift->SUPER::connection; return bless $connection, 'Apache::ConnectionCleanup::RegisterCleanup'; } 1; http://www.modperlcookbook.org/ 549 package Apache::ConnectionCleanup; use 5.006; use strict; use Apache; use Apache::ConnectionCleanup::RegisterCleanup; our @ISA = qw(Apache); sub new {\ my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub connection { my $connection = shift->SUPER::connection; return bless $connection, 'Apache::ConnectionCleanup::RegisterCleanup'; } 1; http://www.modperlcookbook.org/ 550 package Apache::ConnectionCleanup; use 5.006; use strict; use Apache; use Apache::ConnectionCleanup::RegisterCleanup; our @ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub connection { my $connection = shift->SUPER::connection; return bless $connection, 'Apache::ConnectionCleanup::RegisterCleanup'; } 1; http://www.modperlcookbook.org/ 551 package Apache::ConnectionCleanup; use 5.006; use strict; use Apache; use Apache::ConnectionCleanup::RegisterCleanup; our @ISA = qw(Apache); sub new { my ($class, $r) = @_; $r ||= Apache->request; return bless { r => $r }, $class; } sub connection { my $connection = $self->SUPER::connection; return bless $connection, 'Apache::ConnectionCleanup::RegisterCleanup'; } 1; http://www.modperlcookbook.org/ 552 package Apache::ConnectionCleanup::RegisterCleanup; use 5.006; use strict; use warnings; use Apache::Connection; use DynaLoader; our @ISA = qw(DynaLoader Apache::Connection); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); 1; http://www.modperlcookbook.org/ 553 package Apache::ConnectionCleanup::RegisterCleanup; use 5.006; use strict; use warnings; use Apache::Connection; use DynaLoader; our @ISA = qw(DynaLoader Apache::Connection); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); 1; http://www.modperlcookbook.org/ 554 package Apache::ConnectionCleanup::RegisterCleanup; use 5.006; use strict; use warnings; use Apache::Connection; use DynaLoader; our @ISA = qw(DynaLoader Apache::Connection); our $VERSION = '0.01'; __PACKAGE__->bootstrap($VERSION); 1; http://www.modperlcookbook.org/ 555 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 556 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 557 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 558 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 559 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 560 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 561 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 562 #include #include #include #include "EXTERN.h" "perl.h" "XSUB.h" "mod_perl.h" ... static void ApacheConnection_register_cleanup(conn_rec *c, SV *cv) { pool *p = c->pool; ... register_cleanup(p, conn, conn_cleanup_handler, mod_perl_noop); } ... void register_cleanup(conn, cv) Apache::Connection conn SV *cv CODE: ApacheConnection_register_cleanup(conn, cv); http://www.modperlcookbook.org/ 563 http://www.modperlcookbook.org/ 564 But wait, there's more! http://www.modperlcookbook.org/ 565 But wait, there's more! • We mentioned that part of mod_perl fun was overriding core Apache behaviors with your own "devious subsititutes" http://www.modperlcookbook.org/ 566 But wait, there's more! • We mentioned that part of mod_perl fun was overriding core Apache behaviors with your own "devious subsititutes" • Let's do more devious things... http://www.modperlcookbook.org/ 567 Apache Directives http://www.modperlcookbook.org/ 568 Apache Directives • If you have ever typo'd a directive in httpd.conf you've seen something like Invalid command 'Foo', perhaps misspelled or defined by a module not included in the server configuration /usr/local/apache/bin/apachectl start: httpd could not be started http://www.modperlcookbook.org/ 569 Apache Directives • If you have ever typo'd a directive in httpd.conf you've seen something like Invalid command 'Foo', perhaps misspelled or defined by a module not included in the server configuration /usr/local/apache/bin/apachectl start: httpd could not be started • How does Apache know that Foo isn't a real directive? http://www.modperlcookbook.org/ 570 Core Apache is small • Over 200 directives are supported by the standard Apache distribution http://www.modperlcookbook.org/ 571 Core Apache is small • Over 200 directives are supported by the standard Apache distribution • Less that 80 are from core Apache http://www.modperlcookbook.org/ 572 Core Apache is small • Over 200 directives are supported by the standard Apache distribution • Less that 80 are from core Apache • All the rest are from C extension modules http://www.modperlcookbook.org/ 573 Core Apache is small • Over 200 directives are supported by the standard Apache distribution • Less that 80 are from core Apache • All the rest are from C extension modules Alias /perl-bin /usr/local/apache/perl-bin – mod_alias http://www.modperlcookbook.org/ 574 Core Apache is small • Over 200 directives are supported by the standard Apache distribution • Less that 80 are from core Apache • All the rest are from C extension modules Alias /perl-bin /usr/local/apache/perl-bin – mod_alias SetHandler perl-script – mod_mime http://www.modperlcookbook.org/ 575 Core Apache is small • Over 200 directives are supported by the standard Apache distribution • Less that 80 are from core Apache • All the rest are from C extension modules Alias /perl-bin /usr/local/apache/perl-bin – mod_alias SetHandler perl-script – mod_mime PerlHandler – mod_perl http://www.modperlcookbook.org/ 576 How it works http://www.modperlcookbook.org/ 577 How it works • For C modules the process is ordinary http://www.modperlcookbook.org/ 578 How it works • For C modules the process is ordinary – a module registers itself with Apache http://www.modperlcookbook.org/ 579 How it works • For C modules the process is ordinary – a module registers itself with Apache – Apache parses httpd.conf http://www.modperlcookbook.org/ 580 How it works • For C modules the process is ordinary – a module registers itself with Apache – Apache parses httpd.conf – finds a directive http://www.modperlcookbook.org/ 581 How it works • For C modules the process is ordinary – a module registers itself with Apache – Apache parses httpd.conf – finds a directive – looks for a module that agrees to handle the directive http://www.modperlcookbook.org/ 582 How it works • For C modules the process is ordinary – a module registers itself with Apache – Apache parses httpd.conf – finds a directive – looks for a module that agrees to handle the directive – defaults to http_core if no module agrees http://www.modperlcookbook.org/ 583 Data Validation http://www.modperlcookbook.org/ 584 Data Validation • If a module agrees to handle the directive http://www.modperlcookbook.org/ 585 Data Validation • If a module agrees to handle the directive – module tells Apache how the data should be formatted http://www.modperlcookbook.org/ 586 Data Validation • If a module agrees to handle the directive – module tells Apache how the data should be formatted – Apache validates the data format in httpd.conf http://www.modperlcookbook.org/ 587 Data Validation • If a module agrees to handle the directive – module tells Apache how the data should be formatted – Apache validates the data format in httpd.conf – Apache passes the config to the module http://www.modperlcookbook.org/ 588 Data Validation • If a module agrees to handle the directive – module tells Apache how the data should be formatted – Apache validates the data format in httpd.conf – Apache passes the config to the module – module is free to use the data http://www.modperlcookbook.org/ 589 Data Validation • httpd.conf XBitHack One Two, Buckle My Shoe http://www.modperlcookbook.org/ 590 Data Validation • httpd.conf XBitHack One Two, Buckle My Shoe • Apache responds Syntax error on line 19 of /usr/local/apache/conf/httpd.conf: XBitHack takes one argument, Off, On, or Full /usr/local/apache/bin/apachectl start: httpd could not be started http://www.modperlcookbook.org/ 591 Data Validation • httpd.conf XBitHack One Two, Buckle My Shoe • Apache responds Syntax error on line 19 of /usr/local/apache/conf/httpd.conf: XBitHack takes one argument, Off, On, or Full /usr/local/apache/bin/apachectl start: httpd could not be started http://www.modperlcookbook.org/ 592 Directive Handlers http://www.modperlcookbook.org/ 593 Directive Handlers • As with all things, mod_perl provides an API for creating our own Apache directives http://www.modperlcookbook.org/ 594 Directive Handlers • As with all things, mod_perl provides an API for creating our own Apache directives • API is not all that intuitive http://www.modperlcookbook.org/ 595 Directive Handlers • As with all things, mod_perl provides an API for creating our own Apache directives • API is not all that intuitive • Does make sense if you think of the steps Apache has to go through http://www.modperlcookbook.org/ 596 http://www.modperlcookbook.org/ 597 Our own XBitHack http://www.modperlcookbook.org/ 598 Our own XBitHack • Let's re-implement XBitHack in our own Perl module http://www.modperlcookbook.org/ 599 Our own XBitHack • Let's re-implement XBitHack in our own Perl module • Make it usable in for Apache on Win32 http://www.modperlcookbook.org/ 600 Our own XBitHack • Let's re-implement XBitHack in our own Perl module • Make it usable in for Apache on Win32 – on Unix - default to mod_include http://www.modperlcookbook.org/ 601 Our own XBitHack • Let's re-implement XBitHack in our own Perl module • Make it usable in for Apache on Win32 – on Unix - default to mod_include – on Win32 - use our implementation http://www.modperlcookbook.org/ 602 Our own XBitHack • Let's re-implement XBitHack in our own Perl module • Make it usable in for Apache on Win32 – on Unix - default to mod_include – on Win32 - use our implementation PerlModule Cookbook::WinBitHack PerlFixupHandler Cookbook::WinBitHack XBitHack On http://www.modperlcookbook.org/ 603 Step #1 • Define what happens when Apache sees a good XBitHack directive http://www.modperlcookbook.org/ 604 sub XBitHack { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 605 sub XBitHack { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 606 sub XBitHack { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 607 sub XBitHack { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 608 sub XBitHack { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 609 sub XBitHack { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 610 sub XBitHack { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 611 Step #2 • Insert our request time processing http://www.modperlcookbook.org/ 612 Step #2 • Insert our request time processing PerlFixupHandler Cookbook::WinBitHack http://www.modperlcookbook.org/ 613 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 614 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 615 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 616 sub XBitHack ($$$) { my ($cfg, $parms, $arg) = @_; # Let mod_include do the Unix stuff - we only do Win32. return DECLINE_CMD unless $^O =~ m/Win32/; if ($arg =~ m/^(On|Off|Full)$/i) { $cfg->{_state} = uc($arg); } else { die "Invalid XBitHack $arg!"; } } http://www.modperlcookbook.org/ 617 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 618 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 619 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 620 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 621 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 622 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 623 Win32 File Attributes http://www.modperlcookbook.org/ 624 Win32 File Attributes http://www.modperlcookbook.org/ 625 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 626 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 627 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 628 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); return DECLINED unless ( $^O =~ m/Win32/ -f $r->finfo $r->content_type eq 'text/html' $r->allow_options & OPT_INCLUDES $cfg->{_state} ne 'OFF'); && && && && # # # # # we're on Win32 the file exists and is HTML and we have Options +Includes and XBitHack On or Full # Gather the file attributes. my $attr; Win32::File::GetAttributes($r->filename, $attr); # Return DECLINED if the file has the ARCHIVE attribute set, # which is the usual case. return DECLINED if $attr & ARCHIVE(); # Set the Last-Modified header unless the READONLY attribute is set. if ($cfg->{_state} eq 'FULL') { $r->set_last_modified((stat _)[9]) unless $attr & READONLY(); } # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } http://www.modperlcookbook.org/ 629 Step #3 • Let Apache know our module exists http://www.modperlcookbook.org/ 630 More than meets the eye http://www.modperlcookbook.org/ 631 More than meets the eye • As Apache parses httpd.conf we have to let it know to check our Perl module when it encounters a directive http://www.modperlcookbook.org/ 632 More than meets the eye • As Apache parses httpd.conf we have to let it know to check our Perl module when it encounters a directive • The mod_perl API essentially makes our Perl module into a C module using XS http://www.modperlcookbook.org/ 633 More than meets the eye • As Apache parses httpd.conf we have to let it know to check our Perl module when it encounters a directive • The mod_perl API essentially makes our Perl module into a C module using XS • Just a few simple steps in Makefile.PL http://www.modperlcookbook.org/ 634 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 635 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 636 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 637 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 638 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = ( { name errmsg args_how => 'XBitHack', => 'Off, On, or Full', => 'TAKE1', req_override => 'OR_OPTIONS', }, ); command_table(\@directives); http://www.modperlcookbook.org/ 639 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 640 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 641 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 642 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 643 Makefile.PL package Cookbook::WinBitHack; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ); ( => => => => 'XBitHack', 'Off, On, or Full', 'TAKE1', 'OR_OPTIONS', }, command_table(\@directives); http://www.modperlcookbook.org/ 644 Magic http://www.modperlcookbook.org/ 645 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us http://www.modperlcookbook.org/ 646 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 647 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 648 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 649 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 650 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 651 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 652 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 653 Magic • Running perl Makefile.PL has magically created WinBitHack.xs for us static mod_perl_cmd_info cmd_info_XBitHack = { "Cookbook::WinBitHack::XBitHack", "", }; static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; http://www.modperlcookbook.org/ 654 mod_include.c static command_rec mod_cmds[] = { { "XBitHack", perl_cmd_perl_TAKE1, (void*)&cmd_info_XBitHack, OR_OPTIONS, TAKE1, "Off, On, or Full" }, { NULL } }; static const command_rec includes_cmds[] = { {"XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full"}, {NULL} }; http://www.modperlcookbook.org/ 655 DynaLoader package Cookbook::WinBitHack; BEGIN { eval{ require Win32::File; Win32::File->import(qw(READONLY ARCHIVE)); }; } use Apache::Constants qw(OK DECLINED OPT_INCLUDES DECLINE_CMD); use Apache::File; use Apache::ModuleConfig; use DynaLoader; use 5.006; use strict; our $VERSION = '0.01'; our @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); http://www.modperlcookbook.org/ 656 DynaLoader package Cookbook::WinBitHack; BEGIN { eval{ require Win32::File; Win32::File->import(qw(READONLY ARCHIVE)); }; } use Apache::Constants qw(OK DECLINED OPT_INCLUDES DECLINE_CMD); use Apache::File; use Apache::ModuleConfig; use DynaLoader; use 5.006; use strict; our $VERSION = '0.01'; our @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); http://www.modperlcookbook.org/ 657 DynaLoader package Cookbook::WinBitHack; BEGIN { eval{ require Win32::File; Win32::File->import(qw(READONLY ARCHIVE)); }; } use Apache::Constants qw(OK DECLINED OPT_INCLUDES DECLINE_CMD); use Apache::File; use Apache::ModuleConfig; use DynaLoader; use 5.006; use strict; our $VERSION = '0.01'; our @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); http://www.modperlcookbook.org/ 658 DynaLoader package Cookbook::WinBitHack; BEGIN { eval{ require Win32::File; Win32::File->import(qw(READONLY ARCHIVE)); }; } use Apache::Constants qw(OK DECLINED OPT_INCLUDES DECLINE_CMD); use Apache::File; use Apache::ModuleConfig; use DynaLoader; use 5.006; use strict; our $VERSION = '0.01'; our @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); http://www.modperlcookbook.org/ 659 # apachectl start http://www.modperlcookbook.org/ 660 # apachectl start PerlModule Cookbook::WinBitHack http://www.modperlcookbook.org/ 661 # apachectl start PerlModule Cookbook::WinBitHack __PACKAGE__->bootstrap($VERSION); http://www.modperlcookbook.org/ 662 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full http://www.modperlcookbook.org/ 663 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full Cookbook::WinBitHack::XBitHack() http://www.modperlcookbook.org/ 664 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full client request http://www.modperlcookbook.org/ 665 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full client request URI-based init URI translation PerlFixupHandler MIME setting http://www.modperlcookbook.org/ file-based init resource control 666 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full client request URI-based init URI translation Cookbook::WinBitHack::handler() PerlFixupHandler MIME setting http://www.modperlcookbook.org/ file-based init resource control 667 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full client request URI-based init content fixups MIME setting http://www.modperlcookbook.org/ URI translation file-based init resource control 668 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full client request URI-based init mod_include content fixups MIME setting http://www.modperlcookbook.org/ URI translation file-based init resource control 669 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full client request logging content fixups MIME setting http://www.modperlcookbook.org/ URI-based init URI translation file-based init resource control 670 # apachectl start PerlModule Cookbook::WinBitHack XBitHack Full client request logging content fixups MIME setting http://www.modperlcookbook.org/ URI-based init URI translation file-based init resource control 671 http://www.modperlcookbook.org/ 672 Be Proud • What we've just accomplished is pretty amazing http://www.modperlcookbook.org/ 673 Be Proud • What we've just accomplished is pretty amazing – made our Perl module look like an Apache C extension module http://www.modperlcookbook.org/ 674 Be Proud • What we've just accomplished is pretty amazing – made our Perl module look like an Apache C extension module – Altered the behavior of a standard Apache directive to meet new, previously unmet needs http://www.modperlcookbook.org/ 675 Be Proud • What we've just accomplished is pretty amazing – made our Perl module look like an Apache C extension module – Altered the behavior of a standard Apache directive to meet new, previously unmet needs – All with relatively little work http://www.modperlcookbook.org/ 676 Be Proud • What we've just accomplished is pretty amazing – made our Perl module look like an Apache C extension module – Altered the behavior of a standard Apache directive to meet new, previously unmet needs – All with relatively little work • Now, we're doing more http://www.modperlcookbook.org/ 677 More? • If we've gotten here you're all either masochists or I went waaay too fast http://www.modperlcookbook.org/ 678 More? • If we've gotten here you're all either masochists or I went waaay too fast • Let's do another example anyway http://www.modperlcookbook.org/ 679 AddHandler and mod_perl http://www.modperlcookbook.org/ 680 AddHandler and mod_perl • The default AddHandler directive doesn't suit the needs of mod_perl developers http://www.modperlcookbook.org/ 681 AddHandler and mod_perl • The default AddHandler directive doesn't suit the needs of mod_perl developers AddHandler server-parsed .html http://www.modperlcookbook.org/ 682 AddHandler and mod_perl • The default AddHandler directive doesn't suit the needs of mod_perl developers AddHandler server-parsed .html • The problem is that mod_perl requires two directives in order to handle content http://www.modperlcookbook.org/ 683 AddHandler and mod_perl • The default AddHandler directive doesn't suit the needs of mod_perl developers AddHandler server-parsed .html • The problem is that mod_perl requires two directives in order to handle content AddHandler perl-script .html http://www.modperlcookbook.org/ 684 AddHandler and mod_perl • The default AddHandler directive doesn't suit the needs of mod_perl developers AddHandler server-parsed .html • The problem is that mod_perl requires two directives in order to handle content AddHandler perl-script .html PerlHandler Apache::SSI http://www.modperlcookbook.org/ 685 AddHandler and mod_perl • The default AddHandler directive doesn't suit the needs of mod_perl developers AddHandler server-parsed .html • The problem is that mod_perl requires two directives in order to handle content AddHandler perl-script .html PerlHandler Apache::SSI • Wouldn't it be nice to have AddHandler Apache::SSI .html http://www.modperlcookbook.org/ 686 Let's do it ourselves http://www.modperlcookbook.org/ 687 Let's do it ourselves • Some things we need to do... http://www.modperlcookbook.org/ 688 Let's do it ourselves • Some things we need to do... – override the default AddHandler directive http://www.modperlcookbook.org/ 689 Let's do it ourselves • Some things we need to do... – override the default AddHandler directive – schedule various extensions to use various Perl modules http://www.modperlcookbook.org/ 690 Let's do it ourselves • Some things we need to do... – override the default AddHandler directive – schedule various extensions to use various Perl modules – figure out where SetHandler overrides the file extension http://www.modperlcookbook.org/ 691 Makefile.PL http://www.modperlcookbook.org/ 692 package Cookbook::MIMEMapper; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ( => => => => 'AddHandler', 'stash AddHandler settings', 'ITERATE2', 'OR_FILEINFO', }, { name errmsg args_how req_override => => => => 'SetHandler', 'note SetHandler is active', 'TAKE1', 'OR_FILEINFO', }, ); command_table(\@directives); http://www.modperlcookbook.org/ 693 package Cookbook::MIMEMapper; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ( => => => => 'AddHandler', 'stash AddHandler settings', 'ITERATE2', 'OR_FILEINFO', }, { name errmsg args_how req_override => => => => 'SetHandler', 'note SetHandler is active', 'TAKE1', 'OR_FILEINFO', }, ); command_table(\@directives); http://www.modperlcookbook.org/ 694 package Cookbook::MIMEMapper; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ( => => => => 'AddHandler', 'stash AddHandler settings', 'ITERATE2', 'OR_FILEINFO', }, { name errmsg args_how req_override => => => => 'SetHandler', 'note SetHandler is active', 'TAKE1', 'OR_FILEINFO', }, ); command_table(\@directives); http://www.modperlcookbook.org/ 695 ITERATE2 http://www.modperlcookbook.org/ 696 ITERATE2 AddHandler server-parsed .html .shtml http://www.modperlcookbook.org/ 697 ITERATE2 AddHandler server-parsed .html .shtml http://www.modperlcookbook.org/ 698 ITERATE2 AddHandler server-parsed .html .shtml http://www.modperlcookbook.org/ 699 package Cookbook::MIMEMapper; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ( => => => => 'AddHandler', 'stash AddHandler settings', 'ITERATE2', 'OR_FILEINFO', }, { name errmsg args_how req_override => => => => 'SetHandler', 'note SetHandler is active', 'TAKE1', 'OR_FILEINFO', }, ); command_table(\@directives); http://www.modperlcookbook.org/ 700 package Cookbook::MIMEMapper; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ( => => => => 'AddHandler', 'stash AddHandler settings', 'ITERATE2', 'OR_FILEINFO', }, { name errmsg args_how req_override => => => => 'SetHandler', 'note SetHandler is active', 'TAKE1', 'OR_FILEINFO', }, ); command_table(\@directives); http://www.modperlcookbook.org/ 701 package Cookbook::MIMEMapper; use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); use strict; my @directives = { name errmsg args_how req_override ( => => => => 'AddHandler', 'stash AddHandler settings', 'ITERATE2', 'OR_FILEINFO', }, { name errmsg args_how req_override => => => => 'SetHandler', 'note SetHandler is active', 'TAKE1', 'OR_FILEINFO', }, ); command_table(\@directives); http://www.modperlcookbook.org/ 702 The Perl Part http://www.modperlcookbook.org/ 703 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 704 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 705 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 706 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 707 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 708 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 709 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { # AddHandler My::Handler .html push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 710 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 711 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 712 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 713 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 714 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 715 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 716 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; # SetHandler perl-script $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 717 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 718 sub AddHandler { my ($cfg, $parms, $handler, $type) = @_; # Intercept the directive if it looks like a PerlHandler. # This is not an ideal check, but sufficient if ($handler =~ m/::/) { push @{$cfg->{$type}}, $handler; return OK; } # Otherwise let mod_mime handle it. return DECLINE_CMD; } sub SetHandler { my ($cfg, $parms, $handler) = @_; $cfg->{_set_handler} = 1; # We're just marking areas governed by SetHandler. return DECLINE_CMD; } http://www.modperlcookbook.org/ 719 Setup PerlModule Apache::MIMEMapper PerlFixupHandler Apache::MIMEMapper AddHandler Apache::RegistryFilter .pl AddHandler Apache::SSI .html .pl Alias /perl-bin/ /usr/local/apache/perl-bin/ <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlSendHeader On </Location> http://www.modperlcookbook.org/ 720 Setup PerlModule Apache::MIMEMapper PerlFixupHandler Apache::MIMEMapper AddHandler Apache::RegistryFilter .pl AddHandler Apache::SSI .html .pl Alias /perl-bin/ /usr/local/apache/perl-bin/ <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlSendHeader On </Location> http://www.modperlcookbook.org/ 721 Setup PerlModule Apache::MIMEMapper PerlFixupHandler Apache::MIMEMapper AddHandler Apache::RegistryFilter .pl AddHandler Apache::SSI .html .pl Alias /perl-bin/ /usr/local/apache/perl-bin/ <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlSendHeader On </Location> http://www.modperlcookbook.org/ 722 Setup PerlModule Apache::MIMEMapper PerlFixupHandler Apache::MIMEMapper AddHandler Apache::RegistryFilter .pl AddHandler Apache::SSI .html .pl Alias /perl-bin/ /usr/local/apache/perl-bin/ <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlSendHeader On </Location> http://www.modperlcookbook.org/ 723 Setup PerlModule Apache::MIMEMapper PerlFixupHandler Apache::MIMEMapper AddHandler Apache::RegistryFilter .pl AddHandler Apache::SSI .html .pl Alias /perl-bin/ /usr/local/apache/perl-bin/ <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlSendHeader On </Location> http://www.modperlcookbook.org/ 724 Setup PerlModule Apache::MIMEMapper PerlFixupHandler Apache::MIMEMapper AddHandler Apache::RegistryFilter .pl AddHandler Apache::SSI .html .pl Alias /perl-bin/ /usr/local/apache/perl-bin/ <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlSendHeader On </Location> http://www.modperlcookbook.org/ 725 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 726 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 727 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 728 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 729 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 730 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 731 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 732 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 733 sub handler { my $r = shift; my $cfg = Apache::ModuleConfig->get($r, __PACKAGE__); # Also decline if a SetHandler directive is present. # which ought to override any AddHandler settings. return DECLINED if $cfg->{_set_handler}; my ($extension) = $r->filename =~ m!(\.[^.]+)$!; # Set the PerlHandler stack if we have a mapping if (my $handlers = $cfg->{$extension}) { $r->handler('perl-script'); $r->set_handlers(PerlHandler => $handlers); # Notify Apache::Filter if we have more than one $r->dir_config->set(Filter => 'On') if @$handlers > 1; } return OK; } http://www.modperlcookbook.org/ 734 http://www.modperlcookbook.org/ 735 Fine Manuals • Writing Apache Modules with Perl and C – http://www.modperl.com/ • mod_perl Developer's Cookbook – http://www.modperlcookbook.org/ • mod_perl Pocket Reference – http://www.refcards.com/ • mod_perl Guide – http://perl.apache.org/guide/ – http://www.modperlbook.org/ • mod_perl at the ASF – http://perl.apache.org/ http://www.modperlcookbook.org/ 736 Materials These slides http://www.modperlcookbook.org/~geoff/slides/YAPC My modules http://www.modperlcookbook.org/~geoff/modules http://www.modperlcookbook.org/ 737 http://www.modperlcookbook.org/ 738 # apachectl start PerlModule Cookbook::WinBitHack DIR_CREATE SERVER_CREATE XBitHack Full DIR_MERGE SERVER_MERGE request cycle http://www.modperlcookbook.org/ 739
© Copyright 2026 Paperzz