Presentation is loading. Please wait.

Presentation is loading. Please wait.

Programming the Apache Lifecycle

Similar presentations


Presentation on theme: "Programming the Apache Lifecycle"— Presentation transcript:

1 Programming the Apache Lifecycle
Geoffrey Young

2 Overview

3 Overview Apache and mod_perl 101

4 Overview Apache and mod_perl 101 mod_perl Handler Basics

5 Overview Apache and mod_perl 101 mod_perl Handler Basics
Using the Apache Framework

6 Overview Apache and mod_perl 101 mod_perl Handler Basics
Using the Apache Framework Advanced mod_perl API Features

7

8 Apache's Pre-fork Model

9 Apache's Pre-fork Model
Apache parent process

10 Apache's Pre-fork Model
Apache parent process httpd (parent)

11 Apache's Pre-fork Model
Apache parent process forks multiple child processes httpd (parent) httpd (child) httpd (child) httpd (child) httpd (child)

12 Roles and Responsibilities

13 Roles and Responsibilities
httpd parent processes no actual requests

14 Roles and Responsibilities
httpd parent processes no actual requests all requests are served by the child processes

15 Roles and Responsibilities
httpd parent processes no actual requests all requests are served by the child processes requests are handled by any available child process, not necessarily the one that handled the previous request

16 Roles and Responsibilities
httpd parent processes no actual requests all requests are served by the child processes requests are handled by any available child process, not necessarily the one that handled the previous request remember, the HTTP is stateless by default

17 Children are Individuals
httpd (parent) httpd (child) httpd (child) httpd (child) httpd (child)

18 Children are Individuals
httpd (parent) httpd (child) httpd (child) httpd (child) httpd (child)

19 Children are Individuals
httpd (parent) httpd (child) httpd (child) httpd (child) httpd (child)

20 Nice, Responsible Children

21 Nice, Responsible Children
each httpd child processes one incoming request at a time

22 Nice, Responsible Children
each httpd child processes one incoming request at a time only when the request is over is the child free to serve the next request

23 Nice, Responsible Children
each httpd child processes one incoming request at a time only when the request is over is the child free to serve the next request over time httpd child processes are terminated and replaced with fresh children

24 Request Phases

25 Request Phases Apache breaks down request processing into separate, logical parts called phases

26 Request Phases Apache breaks down request processing into separate, logical parts called phases client request logging URI-based init content URI translation fixups file-based init MIME setting resource control

27 Request Phases Apache breaks down request processing into separate, logical parts called phases each request is stepped through the phases until...

28 Request Phases Apache breaks down request processing into separate, logical parts called phases each request is stepped through the phases until... all processing is complete

29 Request Phases Apache breaks down request processing into separate, logical parts called phases each request is stepped through the phases until... all processing is complete somebody throws an "error"

30 Request Phases Apache breaks down request processing into separate, logical parts called phases each request is stepped through the phases until... all processing is complete somebody throws an "error" developers are given the chance to hook into each phase to add custom processing

31 Apache Request Cycle client request

32 Apache Request Cycle client request GET /perl-status HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Cache-Control: no-cache Connection: Keep-Alive, TE Host: User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera [en]

33 Apache Request Cycle client request GET /perl-status HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Cache-Control: no-cache Connection: Keep-Alive, TE Host: User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera [en]

34 Apache Request Cycle client request GET /perl-status HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Cache-Control: no-cache Connection: Keep-Alive, TE Host: User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera [en]

35 Apache Request Cycle client request URI-based init

36 Apache Request Cycle client request URI-based init URI translation

37 Apache Request Cycle client request URI-based init URI translation
file-based init

38 Apache Request Cycle client request URI-based init URI translation
file-based init resource control

39 Apache Request Cycle client request URI-based init URI translation
file-based init MIME setting resource control

40 Apache Request Cycle client request URI-based init URI translation
fixups file-based init MIME setting resource control

41 Apache Request Cycle client request URI-based init content
URI translation fixups file-based init MIME setting resource control

42 Apache Request Cycle content HTTP/1.1 200 OK
Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html

43 Apache Request Cycle content HTTP/1.1 200 OK
Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html

44 Apache Request Cycle content HTTP/1.1 200 OK
Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html

45 Apache Request Cycle client request URI-based init content
URI translation fixups file-based init MIME setting resource control

46 Apache Request Cycle client request logging URI-based init content
URI translation fixups file-based init MIME setting resource control

47 Apache Request Cycle client request logging URI-based init content
URI translation fixups file-based init MIME setting resource control

48 So What? most Apache users don't worry about the request cycle too much...

49 So What? most Apache users don't worry about the request cycle too much... ...but they do use modules that plug into it

50 ... for instance mod_rewrite:
client request URI-based init mod_rewrite: RewriteRule /favicon.ico$ /images/favicon.ico URI translation

51 ... for instance mod_auth: AuthUserFile .htpasswd client request
URI-based init URI translation file-based init mod_auth: AuthUserFile .htpasswd resource control

52 ... for instance mod_cgi: SetHandler cgi-script client request
URI-based init content URI translation fixups file-based init MIME setting resource control

53 That's great breaking down the request into distinct phases has many benefits

54 That's great breaking down the request into distinct phases has many benefits gives each processing point a role that can be easily managed and programmed

55 That's great 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

56 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

57 Enter mod_perl

58 Enter mod_perl mod_perl offers an interface to each phase of the request cycle

59 Enter mod_perl mod_perl offers an interface to each phase of the request cycle opens up the Apache API to Perl code

60 Enter mod_perl mod_perl offers an interface to each phase of the request cycle opens up the Apache API to Perl code allows you to program targeted parts of the request cycle using Perl

61 Enter mod_perl mod_perl offers an interface to each phase of the request cycle opens up the Apache API to Perl code allows you to program targeted parts of the request cycle using Perl we like Perl

62 Apache Request Cycle client request logging URI-based init content
URI translation fixups file-based init MIME setting resource control

63 mod_perl Interface client request PerlCleanupHandler PerlLogHandler
PerlPostReadRequestHandler logging URI-based init PerlHandler PerlTransHandler content URI translation PerlFixupHandler PerlHeaderParserHandler fixups file-based init PerlAccessHandler PerlAuthenHandler PerlAuthzHandler PerlTypeHandler MIME setting resource control

64 mod_perl Interface world's ugliest slide client request
PerlCleanupHandler PerlLogHandler PerlPostReadRequestHandler world's ugliest slide logging URI-based init PerlHandler PerlTransHandler content URI translation PerlFixupHandler PerlHeaderParserHandler fixups file-based init PerlAccessHandler PerlAuthenHandler PerlAuthzHandler PerlTypeHandler MIME setting resource control

65 mod_perl Interface client request PerlCleanupHandler PerlLogHandler
PerlPostReadRequestHandler logging URI-based init PerlHandler PerlTransHandler content URI translation PerlFixupHandler PerlHeaderParserHandler fixups file-based init PerlAccessHandler PerlAuthenHandler PerlAuthzHandler PerlTypeHandler MIME setting resource control

66 What is mod_perl?

67 What is mod_perl? mod_perl is the Perl interface to the Apache API

68 What is mod_perl? mod_perl is the Perl interface to the Apache API
a C extension module, just like mod_cgi or mod_rewrite

69 What is mod_perl? mod_perl is the Perl interface to the Apache API
a C extension module, just like mod_cgi or mod_rewrite creates a persistent perl environment embedded within Apache

70 What's the Big Deal?

71 What's the Big Deal? mod_perl allows you to interact with and directly alter server behavior

72 What's the Big Deal? 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"

73 What's the Big Deal? 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" let's you do it in Perl instead of C

74 What's the Big Deal? 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" let's you do it in Perl instead of C allows you to intercept basic Apache functions and replace them with your own (sometimes devious) Perl substitutes

75 Registry is just a handler

76 Registry is just a handler
Apache::Registry is merely an (incredibly clever and amazing) mod_perl handler

77 Registry is just a handler
Apache::Registry is merely an (incredibly clever and amazing) mod_perl handler its performance gains are made possible due to what mod_perl really is

78 Registry is just a handler
Apache::Registry is merely an (incredibly clever and amazing) mod_perl handler its performance gains are made possible due to what mod_perl really is let's take a peek inside...

79 Apache::Registry

80 Apache::Registry Client side http://localhost/perl-bin/bar.pl

81 Apache::Registry Client side Server side
Server side

82 Apache::Registry Client side Server side
Server side mod_perl intercepts content generation

83 Apache::Registry Client side Server side
Server side mod_perl intercepts content generation for Apache/Registry.pm

84 Apache::Registry Client side Server side
Server side mod_perl intercepts content generation for Apache/Registry.pm calls Apache::Registry::handler(Apache->request)

85 Apache::Registry Client side Server side
Server side mod_perl intercepts content generation for Apache/Registry.pm calls Apache::Registry::handler(Apache->request) inserts wizardry

86 Apache::Registry Client side Server side
Server side mod_perl intercepts content generation for Apache/Registry.pm calls Apache::Registry::handler(Apache->request) inserts wizardry returns response to client

87 Wizardry, you say?

88 Wizardry, you say? the wizardry is basically just putting the CGI script into it's own package

89 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; }; ... your script here... } 1;

90 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; }; ... your script here... } 1;

91 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; }; ... your script here... } 1;

92 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; }; ... your script here... } 1; because the perl interpreter is persistent the (compiled) package is already in memory when called

93

94 "The dream is always the same"

95 "The dream is always the same"

96 "The dream is always the same"
the basic process for mod_perl is the same for the other request phases as for content generation (eg, Registry)

97 "The dream is always the same"
the basic process for mod_perl is the same for the other request phases as for content generation (eg, Registry) Apache passes control to mod_perl

98 "The dream is always the same"
the basic process for mod_perl is the same for the other request phases as for content generation (eg, Registry) Apache passes control to mod_perl mod_perl passes control to your Perl handler

99 "The dream is always the same"
the basic process for mod_perl is the same for the other request phases as for content generation (eg, Registry) Apache passes control to mod_perl mod_perl passes control to your Perl handler your Perl subroutine defines the status

100 "The dream is always the same"
the basic process for mod_perl is the same for the other request phases as for content generation (eg, Registry) Apache passes control to mod_perl mod_perl passes control to your Perl handler your Perl subroutine defines the status mod_perl passes status back to Apache

101 "The dream is always the same"
the basic process for mod_perl is the same for the other request phases as for content generation (eg, Registry) Apache passes control to mod_perl mod_perl passes control to your Perl handler your Perl subroutine defines the status mod_perl passes status back to Apache Apache continues along

102 Apache Request Cycle client request logging URI-based init content
URI translation fixups file-based init MIME setting resource control

103 Key Questions

104 Key Questions What is the Apache default behavior for the phase?

105 Key Questions What is the Apache default behavior for the phase?
What is a typical mod_perl usage of the phase?

106 Key Questions What is the Apache default behavior for the phase?
What is a typical mod_perl usage of the phase? What happens on success?

107 Key Questions What is the Apache default behavior for the phase?
What is a typical mod_perl usage of the phase? What happens on success? What happens on error?

108 The Client Request client request

109 The Client Request client request GET /perl-status HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Cache-Control: no-cache Connection: Keep-Alive, TE Host: User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera [en]

110 The Client Request client request GET /perl-status HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Cache-Control: no-cache Connection: Keep-Alive, TE Host: User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera [en]

111 URI-based Initialization
client request URI-based init

112 URI-based Initialization
Request URI and headers are known

113 URI-based Initialization
Request URI and headers are known Apache request record has been populated

114 URI-based Initialization
Request URI and headers are known Apache request record has been populated The first place you can insert processing

115 URI-based Initialization
Request URI and headers are known Apache request record has been populated The first place you can insert processing Apache has no default behavior

116 URI-based Initialization
Request URI and headers are known Apache request record has been populated The first place you can insert processing Apache has no default behavior All configured handlers are run

117 URI-based Initialization
client request URI-based init

118 URI-based Initialization
client request PerlPostReadRequestHandler

119 URI-based Initialization
client request PerlInitHandler

120 PerlPostReadRequestHandler

121 PerlPostReadRequestHandler
Non-specific hook

122 PerlPostReadRequestHandler
Non-specific hook Useful for adding processing that needs to occur on every request

123 PerlPostReadRequestHandler
Non-specific hook Useful for adding processing that needs to occur on every request every request means every request

124 A sample... Object: to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle

125 A sample... Object: to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle

126 HTTP/1.0 and Host

127 HTTP/1.0 and Host HTTP/1.0 does not require a Host header

128 HTTP/1.0 and Host HTTP/1.0 does not require a Host header
assumes a “one host per IP" configuration

129 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

130 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

131 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0

132 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0

133 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0

134 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 HTTP/ Found Date: Tue, 04 Jun :52:55 GMT Server: Apache/ dev (Unix) Location: Content-Length: 289 Connection: close Content-Type: text/html; charset=iso <!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=" <hr /> <address>Apache/ dev Server at dev.apache.org Port 80</address> </body></html> Connection closed by foreign host.

135 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 HTTP/ Found Date: Tue, 04 Jun :52:55 GMT Server: Apache/ dev (Unix) Location: Content-Length: 289 Connection: close Content-Type: text/html; charset=iso <!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=" <hr /> <address>Apache/ dev Server at dev.apache.org Port 80</address> </body></html> Connection closed by foreign host.

136 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 Host:

137 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 Host:

138 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 Host:

139 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 Host:

140 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 Host: HTTP/ Not Found Date: Tue, 04 Jun :56:40 GMT Server: Apache/ dev (Unix) Content-Length: 283 Content-Type: text/html; charset=iso <!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/ dev Server at Port 80</address> </body></html>

141 Connected to www.apache.org. Escape character is '^]'.
$ telnet 80 Trying Connected to Escape character is '^]'. GET /foo.html HTTP/1.0 Host: HTTP/ Not Found Date: Tue, 04 Jun :56:40 GMT Server: Apache/ dev (Unix) Content-Length: 283 Content-Type: text/html; charset=iso <!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/ dev Server at Port 80</address> </body></html>

142 A sample... Object: to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle

143 A sample... Object: to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle Method: intercept every request prior to content-generation and return an error unless...

144 A sample... Object: to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle Method: intercept every request prior to content-generation and return an error unless... there is a Host header

145 A sample... Object: to protect our name-based virtual hosts from HTTP/1.0 requests Apache can't handle Method: intercept every request prior to content-generation and return an error unless... there is a Host header the request is an absolute URI

146 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;

147 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;

148 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;

149 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;

150 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;

151 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;

152 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;

153 package Cookbook::TrapNoHost;
use Apache::Constants qw(DECLINED BAD_REQUEST); use Apache::URI; use strict; sub handler { my $r = Apache->request; # 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;

154 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;

155 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;

156 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;

157 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;

158 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;

159 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;

160 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;

161 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;

162 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;

163 Setup add TrapNoHost.pm

164 Setup add TrapNoHost.pm to @INC
ServerRoot/lib/perl/Cookbook/TrapNoHost.pm

165 Setup add TrapNoHost.pm to @INC add to httpd.conf
ServerRoot/lib/perl/Cookbook/TrapNoHost.pm add to httpd.conf

166 Setup add TrapNoHost.pm to @INC add to httpd.conf
ServerRoot/lib/perl/Cookbook/TrapNoHost.pm add to httpd.conf PerlModule Cookbook::TrapNoHost

167 Setup add TrapNoHost.pm to @INC add to httpd.conf
ServerRoot/lib/perl/Cookbook/TrapNoHost.pm add to httpd.conf PerlModule Cookbook::TrapNoHost PerlInitHandler Cookbook::TrapNoHost

168 Setup add TrapNoHost.pm to @INC add to httpd.conf that's it!
ServerRoot/lib/perl/Cookbook/TrapNoHost.pm add to httpd.conf PerlModule Cookbook::TrapNoHost PerlInitHandler Cookbook::TrapNoHost that's it!

169 Apache Request Cycle client request logging URI-based init content
URI translation fixups file-based init MIME setting resource control

170 PerlPostReadRequestHandler
Intercept the Request client request PerlPostReadRequestHandler

171 PerlPostReadRequestHandler
Intercept the Request client request PerlPostReadRequestHandler HTTP/ Bad Request Date: Tue, 04 Jun :17:52 GMT Server: Apache/ dev (Unix) mod_perl/1.27_01-dev Perl/v5.8.0 Connection: close Content-Type: text/html; charset=iso Oops! Did you mean to omit a Host header?

172 PerlPostReadRequestHandler
Intercept the Request client request PerlPostReadRequestHandler HTTP/ Bad Request Date: Tue, 04 Jun :17:52 GMT Server: Apache/ dev (Unix) mod_perl/1.27_01-dev Perl/v5.8.0 Connection: close Content-Type: text/html; charset=iso Oops! Did you mean to omit a Host header?

173 PerlPostReadRequestHandler
Intercept the Request client request logging PerlPostReadRequestHandler

174

175 Key Concepts

176 Key Concepts The Apache request object, $r

177 Key Concepts The Apache request object, $r The Apache::Table class

178 Key Concepts The Apache request object, $r The Apache::Table class
Return values and the Apache::Constants class

179 Key Concepts The Apache request object, $r The Apache::Table class
Return values and the Apache::Constants class Stacked handlers

180 Apache Request Object

181 Apache Request Object passed to handlers or available via Apache->request()

182 Apache Request Object passed to handlers or available via Apache->request() $r = shift; # passed to handler()

183 Apache Request Object passed to handlers or available via Apache->request() $r = shift; # passed to handler() $r = Apache->request();

184 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;

185 Apache Request Object passed to handlers or available via Apache->request() $r = shift; # passed to handler() $r = Apache->request();

186 Apache Request Object passed to handlers or available via Apache->request() $r = shift; # passed to handler() $r = Apache->request(); provides access to the Apache class, which provides access to request attributes

187 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;

188 Apache Request Object passed to handlers or available via Apache->request() $r = shift; # passed to handler() $r = Apache->request(); provides access to the Apache class, which provides access to request attributes

189 Apache Request Object passed to handlers or available via Apache->request() $r = shift; # passed to handler() $r = Apache->request(); provides access to the Apache class, which provides access to request attributes singleton-like constructor, always returning the same object

190 Apache::Table

191 Apache::Table the Apache::Table class provides the underlying API for the following request attributes...

192 Apache::Table the Apache::Table class provides the underlying API for the following request attributes... $r->headers_in() $r->headers_out() $r->err_headers_out() $r->dir_config() $r->subprocess_env() $r->notes() Apache::Request::param() Apache::Request::parms() in old releases

193 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;

194 Apache::Table to manipulate Apache::Table objects, you use the provided methods

195 Apache::Table to manipulate Apache::Table objects, you use the provided methods get() set() add() unset() do() merge() new() clear()

196 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;

197 Apache Table Properties

198 Apache Table Properties
Apache tables have some nice properties

199 Apache Table Properties
Apache tables have some nice properties case insensitive

200 Apache Table Properties
Apache tables have some nice properties case insensitive allow for multi-valued keys

201 Apache Table Properties
Apache tables have some nice properties case insensitive allow for multi-valued keys they also have one important limitation

202 Apache Table Properties
Apache tables have some nice properties case insensitive allow for multi-valued keys they also have one important limitation can contain only simple strings

203 Apache Table Properties
Apache tables have some nice properties case insensitive allow for multi-valued keys they also have one important limitation can contain only simple strings use pnotes() for Perl scalars

204 keeps the Net flowin'...

205 keeps the Net flowin'... both case-insensitivity and multiple values are key to manipulating headers

206 keeps the Net flowin'... both case-insensitivity and multiple values are key to manipulating headers $r->headers_out->set('Set-Cookie' => 'name=foo');

207 keeps the Net flowin'... both case-insensitivity and multiple values are key to manipulating headers $r->headers_out->set('Set-Cookie' => 'name=foo'); $r->headers_out->add('set-cookie' => 'name=bar');

208 keeps the Net flowin'... both case-insensitivity and multiple values are key to manipulating headers $r->headers_out->set('Set-Cookie' => 'name=foo'); $r->headers_out->add('set-cookie' => 'name=bar'); = $r->headers_out->get('Set-cookie');

209 Table Iteration

210 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table

211 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table my $input = $apr->param; # Apache::Table object $input->do(sub { my ($key, $value) $log->info("input: name = $key, value = $value"); 1; });

212 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table my $input = $apr->param; # Apache::Table object $input->do(sub { my ($key, $value) $log->info("input: name = $key, value = $value"); 1; });

213 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table my $input = $apr->param; # Apache::Table object $input->do(sub { my ($key, $value) $log->info("input: name = $key, value = $value"); 1; });

214 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table my $input = $apr->param; # Apache::Table object $input->do(sub { my ($key, $value) $log->info("input: name = $key, value = $value"); 1; });

215 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table my $input = $apr->param; # Apache::Table object $input->do(sub { my ($key, $value) $log->info("input: name = $key, value = $value"); 1; });

216 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table my $input = $apr->param; # Apache::Table object $input->do(sub { my ($key, $value) $log->info("input: name = $key, value = $value"); 1; });

217 Table Iteration Apache::Table objects use a special idiom when you need to operate on every item in a table my $input = $apr->param; # Apache::Table object $input->do(sub { my ($key, $value) $log->info("input: name = $key, value = $value"); 1; });

218 More Apache::Table Fun

219 More Apache::Table Fun
Tired

220 More Apache::Table Fun
Tired PerlSetVar Sails "jib"

221 More Apache::Table Fun
Tired PerlSetVar Sails "jib" my $sail = $r->dir_config('Sails');

222 More Apache::Table Fun
Tired PerlSetVar Sails "jib" my $sail = $r->dir_config('Sails'); Wired PerlSetVar Sails "spinnaker"

223 More Apache::Table Fun
Tired PerlSetVar Sails "jib" my $sail = $r->dir_config('Sails'); Wired PerlSetVar Sails "spinnaker" PerlAddVar Sails "blooper"

224 More Apache::Table Fun
Tired PerlSetVar Sails "jib" my $sail = $r->dir_config('Sails'); Wired PerlSetVar Sails "spinnaker" PerlAddVar Sails "blooper" = $r->dir_config->get('Sails');

225 Trickery

226 Trickery really understanding the Apache::Table class will make you a better mod_perl programmer

227 Trickery really understanding the Apache::Table class will make you a better mod_perl programmer every table can be set

228 Trickery really understanding the Apache::Table class will make you a better mod_perl programmer every table can be set $r->headers_in() $r->headers_out() $r->err_headers_out() $r->dir_config() $r->subprocess_env() $r->notes() Apache::Request::param()

229 Trickery handle "what if?" cases

230 Trickery handle "what if?" cases
$r->headers_in->set('Set-Cookie' => 'name=foo'); my $sub = $r->lookup_uri('/scripts/foo.html');

231 Trickery handle "what if?" cases
$r->headers_in->set('Set-Cookie' => 'name=foo'); my $sub = $r->lookup_uri('/scripts/foo.html');

232 Trickery handle "what if?" cases gratuitous exploitation
$r->headers_in->set('Set-Cookie' => 'name=foo'); my $sub = $r->lookup_uri('/scripts/foo.html'); gratuitous exploitation

233 Trickery handle "what if?" cases gratuitous exploitation
$r->headers_in->set('Set-Cookie' => 'name=foo'); my $sub = $r->lookup_uri('/scripts/foo.html'); gratuitous exploitation # configure "PerlSetVar Filter On" on-the-fly

234 Trickery handle "what if?" cases gratuitous exploitation
$r->headers_in->set('Set-Cookie' => 'name=foo'); my $sub = $r->lookup_uri('/scripts/foo.html'); gratuitous exploitation # configure "PerlSetVar Filter On" on-the-fly $r->dir_config->set(Filter => 'On');

235 Apache::Constants

236 Apache::Constants Apache::Constants class provides over 90 runtime constants use Apache::Constants qw(DECLINED BAD_REQUEST);

237 Apache::Constants Apache::Constants class provides over 90 runtime constants use Apache::Constants qw(DECLINED BAD_REQUEST); the most common are:

238 Apache::Constants Apache::Constants class provides over 90 runtime constants use Apache::Constants qw(DECLINED BAD_REQUEST); the most common are: OK

239 Apache::Constants Apache::Constants class provides over 90 runtime constants use Apache::Constants qw(DECLINED BAD_REQUEST); the most common are: OK SERVER_ERROR

240 Apache::Constants Apache::Constants class provides over 90 runtime constants use Apache::Constants qw(DECLINED BAD_REQUEST); the most common are: OK SERVER_ERROR REDIRECT

241 Apache::Constants Apache::Constants class provides over 90 runtime constants use Apache::Constants qw(DECLINED BAD_REQUEST); the most common are: OK SERVER_ERROR REDIRECT DECLINED

242 Return Values handlers are expected to return a value

243 Return Values handlers are expected to return a value
the return value of the handler defines the status of the request

244 Return Values handlers are expected to return a value
the return value of the handler defines the status of the request Apache defines three "good" return values

245 Return Values handlers are expected to return a value
the return value of the handler defines the status of the request Apache defines three "good" return values OK – all is well

246 Return Values handlers are expected to return a value
the return value of the handler defines the status of the request Apache defines three "good" return values OK – all is well DECLINED – forget about me

247 Return Values handlers are expected to return a value
the return value of the handler defines the status of the request Apache defines three "good" return values OK – all is well DECLINED – forget about me DONE – we're finished, start to log

248 Return Values handlers are expected to return a value
the return value of the handler defines the status of the request Apache defines three "good" return values OK – all is well DECLINED – forget about me DONE – we're finished, start to log All other values are "errors" and trigger the ErrorDocument cycle

249 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;

250 When handlers turn bad...

251 When handlers turn bad... "error" return codes are not always errors

252 When handlers turn bad... "error" return codes are not always errors
instead, they indicate a new route for the request

253 When handlers turn bad... "error" return codes are not always errors
instead, they indicate a new route for the request errors codes take effect immediately

254 When handlers turn bad... "error" return codes are not always errors
instead, they indicate a new route for the request errors codes take effect immediately other scheduled handlers are not run

255 package My::Redirect; use Apache::Constants qw(REDIRECT);
sub handler { my $r = shift; $r->headers_out->set(Location => '/foo'); return REDIRECT; }; 1;

256 package My::Redirect; use Apache::Constants qw(REDIRECT);
sub handler { my $r = shift; $r->headers_out->set(Location => '/foo'); return REDIRECT; }; 1;

257 package My::Redirect; use Apache::Constants qw(REDIRECT);
sub handler { my $r = shift; $r->headers_out->set(Location => '/foo'); return REDIRECT; }; 1;

258 package My::Redirect; use Apache::Constants qw(REDIRECT);
sub handler { my $r = shift; $r->headers_out->set(Location => '/foo'); return REDIRECT; }; 1; <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <HTML><HEAD> <TITLE>302 Found</TITLE> </HEAD><BODY> <H1>Found</H1> The document has moved <A HREF="/foo">here</A>.<P> <HR> <ADDRESS>Apache/ dev Server at mainsheet.laserlink.com Port 80</ADDRESS> </BODY></HTML>

259 package My::Redirect; use Apache::Constants qw(REDIRECT);
sub handler { my $r = shift; $r->headers_out->set(Location => '/foo'); return REDIRECT; }; 1; <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <HTML><HEAD> <TITLE>302 Found</TITLE> </HEAD><BODY> <H1>Found</H1> The document has moved <A HREF="/foo">here</A>.<P> <HR> <ADDRESS>Apache/ dev Server at mainsheet.laserlink.com Port 80</ADDRESS> </BODY></HTML>

260 Stacked Handlers

261 Stacked Handlers Apache allows for more than one module to handle each phase

262 Stacked Handlers Apache allows for more than one module to handle each phase except content-generation

263 Stacked Handlers Apache allows for more than one module to handle each phase except content-generation (in 1.3)

264 Stacked Handlers Apache allows for more than one module to handle each phase except content-generation (in 1.3) For example, both mod_rewrite and mod_alias schedule fixup processing

265 Stacked Handlers Apache allows for more than one module to handle each phase except content-generation (in 1.3) For example, both mod_rewrite and mod_alias schedule fixup processing mod_perl is just another Apache module

266 Stacked Handlers Apache allows for more than one module to handle each phase except content-generation (in 1.3) For example, both mod_rewrite and mod_alias schedule fixup processing mod_perl is just another Apache module happens to schedule processing for every phase

267 Perl Stacked Handlers

268 Perl Stacked Handlers for each phase of the request, mod_perl will run any registered Perl handlers for that phase

269 Perl Stacked Handlers for each phase of the request, mod_perl will run any registered Perl handlers for that phase you can register more than one Perl handler per phase

270 Perl Stacked Handlers for each phase of the request, mod_perl will run any registered Perl handlers for that phase you can register more than one Perl handler per phase whether all handlers are called depends on the syntax of the phase itself in Apache

271 Perl Stacked Handlers some phases run until the handler list is exhausted

272 Perl Stacked Handlers some phases run until the handler list is exhausted PerlInitHandler Cookbook::TrapNoHost PerlInitHandler Apache::Reload

273 Perl Stacked Handlers some phases run until the handler list is exhausted PerlInitHandler Cookbook::TrapNoHost PerlInitHandler Apache::Reload some phases run until one handler returns OK

274 Perl Stacked Handlers some phases run until the handler list is exhausted PerlInitHandler Cookbook::TrapNoHost PerlInitHandler Apache::Reload some phases run until one handler returns OK PerlTransHandler My::TranslateHTML PerlTransHandler My::TranslateText

275 Perl Stacked Handlers some phases run until the handler list is exhausted PerlInitHandler Cookbook::TrapNoHost PerlInitHandler Apache::Reload some phases run until one handler returns OK PerlTransHandler My::TranslateHTML PerlTransHandler My::TranslateText all phases terminate on "error"

276 Key Concepts The Apache request object, $r The Apache::Table class
Return values and the Apache::Constants class Stacked handlers

277

278 URI Translation client request URI-based init URI translation

279 URI Translation Apache needs to map the URI to a physical file on disk

280 URI Translation Apache needs to map the URI to a physical file on disk
Default is to prepend DocumentRoot to the URI DocumentRoot /usr/local/apache/htdocs

281 URI Translation Directives like Alias override the default

282 URI Translation Directives like Alias override the default
DocumentRoot /usr/local/apache/htdocs Alias /manual/ /usr/local/apache/manual/" <Directory /usr/local/apache/manual> ...

283 URI Translation Directives like Alias override the default
DocumentRoot /usr/local/apache/htdocs Alias /manual/ /usr/local/apache/manual/ <Directory /usr/local/apache/manual> ... Some URIs have no associated file, but Apache tries anyway

284 URI Translation Directives like Alias override the default
DocumentRoot /usr/local/apache/htdocs Alias /manual/ /usr/local/apache/manual/ <Directory /usr/local/apache/manual> ... Some URIs have no associated file, but Apache tries anyway <Location server-status>

285 URI Translation client request URI-based init URI translation

286 URI Translation client request URI-based init PerlTransHandler

287 PerlTransHandler

288 PerlTransHandler Useful for overriding the Apache default

289 PerlTransHandler Useful for overriding the Apache default
Allows you to be extremely devious

290 PerlTransHandler Useful for overriding the Apache default
Allows you to be extremely devious There are a few pitfalls of which to be aware

291 Simple PerlTransHandler

292 Simple PerlTransHandler
Object: be rid of those silly favicon.ico requests that end up 404

293 Simple PerlTransHandler
Object: be rid of those silly favicon.ico requests that end up 404 Method: translate the incoming URI to a common place if it matches favicon.ico

294 package Cookbook::Favicon; use Apache::Constants qw(DECLINED);
use strict; sub handler { my $r = shift; $r->uri("/images/favicon.ico") if $r->uri =~ m!/favicon\.ico$!; return DECLINED; } 1;

295 package Cookbook::Favicon; use Apache::Constants qw(DECLINED);
use strict; sub handler { my $r = shift; $r->uri("/images/favicon.ico") if $r->uri =~ m!/favicon\.ico$!; return DECLINED; } 1;

296 package Cookbook::Favicon; use Apache::Constants qw(DECLINED);
use strict; sub handler { my $r = shift; $r->uri("/images/favicon.ico") if $r->uri =~ m!/favicon\.ico$!; return DECLINED; } 1;

297 Setup

298 Setup add Favicon.pm

299 Setup add Favicon.pm to @INC ServerRoot/lib/perl/Cookbook/Favicon.pm

300 Setup add Favicon.pm to @INC add to httpd.conf
ServerRoot/lib/perl/Cookbook/Favicon.pm add to httpd.conf

301 Setup add Favicon.pm to @INC add to httpd.conf
ServerRoot/lib/perl/Cookbook/Favicon.pm add to httpd.conf PerlModule Cookbook::Favicon

302 Setup add Favicon.pm to @INC add to httpd.conf
ServerRoot/lib/perl/Cookbook/Favicon.pm add to httpd.conf PerlModule Cookbook::Favicon PerlTransHandler Cookbook::Favicon

303 Setup add Favicon.pm to @INC add to httpd.conf that's it!
ServerRoot/lib/perl/Cookbook/Favicon.pm add to httpd.conf PerlModule Cookbook::Favicon PerlTransHandler Cookbook::Favicon that's it!

304 Why Not Use mod_rewrite?

305 Why Not Use mod_rewrite?
our Cookbook::Favicon is pretty much the same as RewriteRule /favicon.ico$ /images/favicon.ico

306 Why Not Use mod_rewrite?
our Cookbook::Favicon is pretty much the same as RewriteRule /favicon.ico$ /images/favicon.ico Let's look at a more clever example...

307 Mischievous Behavior

308 Mischievous Behavior Simple URI re-mapping is only the beginning

309 Mischievous Behavior Simple URI re-mapping is only the beginning
Apache has this neat, built-in functionality called proxying

310 Mischievous Behavior Simple URI re-mapping is only the beginning
Apache has this neat, built-in functionality called proxying provided you have mod_proxy installed

311 Mischievous Behavior Simple URI re-mapping is only the beginning
Apache has this neat, built-in functionality called proxying provided you have mod_proxy installed With mod_perl and mod_proxy you can proxy just about anything...

312 Advanced PerlTransHandler
Object: create a proxy that uses our local Apache documentation instead of ASF servers

313 Advanced PerlTransHandler
Object: create a proxy that uses our local Apache documentation instead of ASF servers Method: intercept proxy requests and silently replace calls to with /usr/local/apache/htdocs/manual

314 Client Setup

315 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

316 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

317 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

318 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

319 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

320 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

321 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

322 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

323 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

324 package My::ManualProxy; use Apache::Constants qw(OK DECLINED);
use strict; sub handler { my $r = shift; return DECLINED unless $r->proxyreq; my (undef, $file) = $r->uri =~ m!^ if ($file =~ m!^docs/!) { $file =~ s!^docs/!manual/!; $file = join "/", ($r->document_root, $file); if (-f $file) { $r->filename($file); # use local disk $r->proxyreq(0); # fool mod_mime return OK; } return DECLINED; 1;

325 Proxy in Action GET HTTP/1.1 Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: httpd.apache.org User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera 5.12 [en] HTTP/ OK Last-Modified: Sat, 25 May :15:27 GMT ETag: "240c cf00cff" Accept-Ranges: bytes Content-Length: 13152 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/html

326 Proxy in Action GET HTTP/1.1 Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: httpd.apache.org User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera 5.12 [en] HTTP/ OK Last-Modified: Sat, 25 May :15:27 GMT ETag: "240c cf00cff" Accept-Ranges: bytes Content-Length: 13152 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/html

327 Winner Takes All

328 Winner Takes All

329 Winner Takes All The first URI translation handler to return OK ends the phase

330 Winner Takes All The first URI translation handler to return OK ends the phase for both mod_perl and Apache!

331 Winner Takes All The first URI translation handler to return OK ends the phase for both mod_perl and Apache! Return OK only when you map the file to disk yourself

332 Winner Takes All The first URI translation handler to return OK ends the phase for both mod_perl and Apache! Return OK only when you map the file to disk yourself Return DECLINED all other times

333 File-based Initialization
client request URI-based init URI translation file-based init

334 File-based Initialization
URI has been mapped to a file $r->filename is now known

335 File-based Initialization
URI has been mapped to a file $r->filename is now known We also know to which <Location> the request belongs

336 File-based Initialization
URI has been mapped to a file $r->filename is now known We also know to which <Location> the request belongs Apache has no default behavior

337 File-based Initialization
URI has been mapped to a file $r->filename is now known We also know to which <Location> the request belongs Apache has no default behavior All configured handlers are run

338 File-based Initialization
client request URI-based init URI translation file-based init

339 File-based Initialization
client request URI-based init URI translation PerlHeaderParserHandler

340 File-based Initialization
client request URI-based init URI translation PerlInitHandler

341 PerlHeaderParserHandler

342 PerlHeaderParserHandler
Non-specific hook

343 PerlHeaderParserHandler
Non-specific hook Useful for adding processing that needs to occur on every request to a given URI

344 Sample Usage...

345 Sample Usage... Parsing out the query string or POST data on each request is a pain

346 Sample Usage... Parsing out the query string or POST data on each request is a pain For the most part, you know you need it to every request to a given <Location>

347 Sample Usage... Parsing out the query string or POST data on each request is a pain For the most part, you know you need it to every request to a given <Location> Modularize the parsing code

348 Apache::RequestNotes

349 Apache::RequestNotes
Apache::RequestNotes parses cookies and input parameters

350 Apache::RequestNotes
Apache::RequestNotes parses cookies and input parameters stores the data in pnotes() for later retrieval

351 Interface

352 Interface in httpd.conf

353 Interface in httpd.conf Alias /perl-bin /usr/local/apache/perl-bin
<Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlInitHandler Apache::RequestNotes </Location>

354 Interface in httpd.conf Alias /perl-bin /usr/local/apache/perl-bin
<Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlInitHandler Apache::RequestNotes </Location>

355 Interface in httpd.conf in handler
Alias /perl-bin /usr/local/apache/perl-bin <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlInitHandler Apache::RequestNotes </Location> in handler

356 Interface in httpd.conf in handler
Alias /perl-bin /usr/local/apache/perl-bin <Location /perl-bin/> SetHandler perl-script PerlHandler Apache::Registry Options +ExecCGI PerlInitHandler Apache::RequestNotes </Location> in handler my $input = $r->pnotes('INPUT'); # Apache::Table reference my $uploads = $r->pnotes('UPLOADS'); # Apache::Upload array ref my $cookies = $r->pnotes('COOKIES'); # hash reference

357 Resource Control client request URI-based init URI translation
file-based init resource control

358 Resource Control

359 Resource Control Request is inside a particular <Location> container

360 Resource Control Request is inside a particular <Location> container Apache provides three different layers of control to determine who gets access to the resource

361 Resource Control Request is inside a particular <Location> container Apache provides three different layers of control to determine who gets access to the resource Client access checker

362 Resource Control Request is inside a particular <Location> container Apache provides three different layers of control to determine who gets access to the resource Client access checker User ID checker

363 Resource Control Request is inside a particular <Location> container Apache provides three different layers of control to determine who gets access to the resource Client access checker User ID checker User authorization checker

364 Resource Control client request URI-based init URI translation
file-based init resource control

365 Resource Control client request URI-based init URI translation
file-based init Client Access User ID User Authorization

366 Resource Control client request URI-based init URI translation
file-based init PerlAccessHandler PerlAuthenHandler PerlAuthzHandler

367 User Access

368 User Access Used to make access decisions based on Client information

369 User Access Used to make access decisions based on Client information
Client IP

370 User Access Used to make access decisions based on Client information
Client IP Client User-Agent

371 User Access Used to make access decisions based on Client information
Client IP Client User-Agent Request URI

372 User Access Used to make access decisions based on Client information
Client IP Client User-Agent Request URI mod_access controls Apache's default

373 User Access Used to make access decisions based on Client information
Client IP Client User-Agent Request URI mod_access controls Apache's default Allow from localhost

374 User Access Used to make access decisions based on Client information
Client IP Client User-Agent Request URI mod_access controls Apache's default Allow from localhost All configured handlers run

375 PerlAccessHandler

376 PerlAccessHandler Useful for making same decisions as mod_auth

377 PerlAccessHandler Useful for making same decisions as mod_auth
do it in Perl

378 Simple PerlAccessHandler

379 Simple PerlAccessHandler
Object: get debugging telnet sessions past Basic authentication

380 Simple PerlAccessHandler
Object: get debugging telnet sessions past Basic authentication Method: set the Authorization header to a known user if coming from localhost

381 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

382 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

383 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

384 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

385 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

386 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

387 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

388 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

389 package My::DefaultLogin; use Apache::Constants qw(OK);
use MIME::Base64 (); use Socket qw(sockaddr_in inet_ntoa); use strict; sub handler { my $r = shift; my $c = $r->connection; my $local_ip = inet_ntoa((sockaddr_in($c->local_addr))[1]); if ($c->remote_ip eq $local_ip) { my $user = 'bug'; my $passwd = 'squashing'; # Join user and password and set the incoming header. my $credentials = MIME::Base64::encode(join(':', $user, $passwd)); $r->headers_in->set(Authorization => "Basic $credentials"); } return OK; 1;

390 Setup add DefaultLogin.pm to @INC add to httpd.conf that's it!
ServerRoot/lib/perl/My/DefaultLogin.pm add to httpd.conf PerlModule My::DefaultLogin PerlAccessHandler My::DefaultLogin that's it!

391 How to Deny Access

392 How to Deny Access Each access handler returns OK if the client meets its conditions

393 How to Deny Access Each access handler returns OK if the client meets its conditions Access handlers return FORBIDDEN to decline access

394 Reality... In the real world, you could accomplish the same thing with the core Satisfy directive

395 Reality... In the real world, you could accomplish the same thing with the core Satisfy directive AuthType Basic AuthName "cookbook" AuthUserFile .htpasswd Require valid-user PerlAccessHandler My::DefaultLogin

396 Reality... In the real world, you could accomplish the same thing with the core Satisfy directive AuthType Basic AuthName "cookbook" AuthUserFile .htpasswd Require valid-user Allow from localhost Satisfy any

397 Resource Control client request URI-based init URI translation
file-based init Client Access User ID

398 User Authentication

399 User Authentication Apache default authentication mechanism is mod_auth

400 User Authentication Apache default authentication mechanism is mod_auth Winner takes all

401 User Authentication Apache default authentication mechanism is mod_auth Winner takes all uses a password file generated using Apache's htpasswd utility

402 User Authentication Apache default authentication mechanism is mod_auth Winner takes all uses a password file generated using Apache's htpasswd utility geoff:zzpEyL0tbgwwk

403 User Authentication configuration placed in .htaccess file or httpd.conf

404 User Authentication configuration placed in .htaccess file or httpd.conf AuthUserFile .htpasswd AuthName "cookbook" AuthType Basic Require valid-user

405 User Authentication configuration placed in .htaccess file or httpd.conf AuthUserFile .htpasswd AuthName "cookbook" AuthType Basic Require valid-user

406 How Authentication Works
client requests a document

407 How Authentication Works
client requests a document GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US)

408 How Authentication Works
client requests a document GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) server denies request

409 How Authentication Works
client requests a document GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) server denies request HTTP/ Authorization Required WWW-Authenticate: Basic realm="my site" Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html; charset=iso

410 How Authentication Works
client requests a document GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) server denies request HTTP/ Authorization Required WWW-Authenticate: Basic realm="my site" Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html; charset=iso

411 How Authentication Works
client request client request URI-based init URI translation file-based init Client Access User ID

412 How Authentication Works
client request client request URI-based init URI translation file-based init Client Access User ID HTTP/ Authorization Required

413 How Authentication Works
client request client request logging URI-based init URI translation file-based init Client Access User ID

414 How Authentication Works
client sends a new request

415 How Authentication Works
client sends a new request GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Authorization: Basic Z2VvZmY6YWZha2VwYXNzd29yZA== Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US)

416 How Authentication Works
client sends a new request GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Authorization: Basic Z2VvZmY6YWZha2VwYXNzd29yZA== Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US)

417 How Authentication Works
client sends a new request GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Authorization: Basic Z2VvZmY6YWZha2VwYXNzd29yZA== Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) server sends document

418 How Authentication Works
client sends a new request GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Authorization: Basic Z2VvZmY6YWZha2VwYXNzd29yZA== Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) server sends document HTTP/ OK Keep-Alive: timeout=15, max=99 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html

419 Resource Control client request URI-based init URI translation
file-based init Client Access User ID

420 Resource Control client request URI-based init URI translation
file-based init Client Access PerlAuthenHandler

421 Who Uses Flat Files?

422 Who Uses Flat Files? flat files are limiting, hard to manage, difficult to integrate, and just plain boring

423 Who Uses Flat Files? flat files are limiting, hard to manage, difficult to integrate, and just plain boring we can use the Apache API and Perl to replace flat files with our own authentication mechanism

424 Do it in Perl since mod_perl gives us the ability to intercept the request cycle before Apache, we can authenticate using Perl instead

425 Do it in Perl since mod_perl gives us the ability to intercept the request cycle before Apache, we can authenticate using Perl instead Apache provides an API, making the job easy

426 Do it in Perl since mod_perl gives us the ability to intercept the request cycle before Apache, we can authenticate using Perl instead Apache provides an API, making the job easy mod_perl provides access to the Apache API

427 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

428 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

429 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

430 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

431 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

432 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

433 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

434 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

435 No Authorization Header
client requests a document GET /perl-status HTTP/1.1 Accept: text/xml, image/png, image/jpeg, image/gif, text/plain Accept-Charset: ISO , utf-8;q=0.66, *;q=0.66 Accept-Encoding: gzip, deflate, compress;q=0.9 Accept-Language: en-us Connection: keep-alive Host: Keep-Alive: 300 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) server denies request HTTP/ Authorization Required WWW-Authenticate: Basic realm="my site" Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html; charset=iso

436 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

437 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

438 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

439 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

440 package My::Authenticate;
use Apache::Constants qw(OK DECLINED AUTH_REQUIRED); use strict; sub handler { my $r = shift; # Let subrequests pass. return DECLINED unless $r->is_initial_req; # 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; }

441 Configuration

442 Configuration change AuthUserFile .htpasswd AuthName "cookbook"
AuthType Basic Require valid-user

443 Configuration change to AuthUserFile .htpasswd AuthName "cookbook"
AuthType Basic Require valid-user to PerlAuthenHandler My::Authenticate

444 Configuration change to AuthUserFile .htpasswd AuthName "cookbook"
AuthType Basic Require valid-user to PerlAuthenHandler My::Authenticate

445 The Choice is Yours how you decide to authenticate is now up to you

446 The Choice is Yours how you decide to authenticate is now up to you
sub authenticate_user { my ($user, $pass) return $user eq $pass; }

447 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?

448 The Power of CPAN over 25 Apache:: shrink-wrapped modules on CPAN for authentication SecureID Radius SMB LDAP NTLM

449 To Infinity and Beyond!

450 To Infinity and Beyond!

451 To Infinity and Beyond! this example only covered Basic authentication via popup box

452 To Infinity and Beyond! this example only covered Basic authentication via popup box the same techniques can be used to authenticate via a login form plus cookies, munged URLs, or hidden fields

453 To Infinity and Beyond! this example only covered Basic authentication via popup box the same techniques can be used to authenticate via a login form plus cookies, munged URLs, or hidden fields extended to use Digest authentication as well

454

455 User Authorization client request URI-based init URI translation
file-based init Client Access User ID User Authorization

456 User Authorization We now know the user has supplied a valid password

457 User Authorization We now know the user has supplied a valid password
now it's up to us to decide if we want the user to have access

458 User Authorization

459 User Authorization Apache's default behavior varies, depending on the syntax of Require Require valid-user Require user foo Require group bar

460 User Authorization Apache's default behavior varies, depending on the syntax of Require Require valid-user Require user foo Require group bar Require file-owner Require file-group

461 User Authorization Apache's default behavior varies, depending on the syntax of Require Require valid-user Require user foo Require group bar Require file-owner Require file-group Winner takes all

462 User Authorization client request URI-based init URI translation
file-based init Client Access User ID User Authorization

463 User Authorization client request URI-based init URI translation
file-based init Client Access User ID PerlAuthzHandler

464 PerlAuthzHandler

465 PerlAuthzHandler Key is the requires() method

466 PerlAuthzHandler Key is the requires() method
$r->requires() returns an array of hashes representing all Require directives

467 PerlAuthzHandler Key is the requires() method
$r->requires() returns an array of hashes representing all Require directives Require user grier ryan Require group admiral

468 PerlAuthzHandler Key is the requires() method
$r->requires() returns an array of hashes representing all Require directives Require user grier ryan Require group admiral [ { requirement => `user grier ryan', method => -1}, { requirement => `group admiral', ];

469 PerlAuthzHandler Once you get the Require directive back, you can decide which users meet the authorization requirement

470 PerlAuthzHandler Once you get the Require directive back, you can decide which users meet the authorization requirement foreach my $requires { my = split " ", $requires->{requirement}; # We're ok if only valid-user was required. return OK if lc($directive) eq 'valid-user'; # Likewise if the user requirement was specified and # we match based on what we already know. return OK if lc($directive) eq 'user' && grep { $_ eq $r->user }

471 PerlAuthzHandler Once you get the Require directive back, you can decide which users meet the authorization requirement foreach my $requires { my = split " ", $requires->{requirement}; # We're ok if only valid-user was required. return OK if lc($directive) eq 'valid-user'; # Likewise if the user requirement was specified and # we match based on what we already know. return OK if lc($directive) eq 'user' && grep { $_ eq $r->user }

472 PerlAuthzHandler Once you get the Require directive back, you can decide which users meet the authorization requirement foreach my $requires { my = split " ", $requires->{requirement}; # We're ok if only valid-user was required. return OK if lc($directive) eq 'valid-user'; # Likewise if the user requirement was specified and # we match based on what we already know. return OK if lc($directive) eq 'user' && grep { $_ eq $r->user } [ { requirement => `user grier ryan', method => -1} ]

473 PerlAuthzHandler Once you get the Require directive back, you can decide which users meet the authorization requirement foreach my $requires { my = split " ", $requires->{requirement}; # We're ok if only valid-user was required. return OK if lc($directive) eq 'valid-user'; # Likewise if the user requirement was specified and # we match based on what we already know. return OK if lc($directive) eq 'user' && grep { $_ eq $r->user } [ { requirement => `user grier ryan', method => -1} ]

474 PerlAuthzHandler Once you get the Require directive back, you can decide which users meet the authorization requirement foreach my $requires { my = split " ", $requires->{requirement}; # We're ok if only valid-user was required. return OK if lc($directive) eq 'valid-user'; # Likewise if the user requirement was specified and # we match based on what we already know. return OK if lc($directive) eq 'user' && grep { $_ eq $r->user } [ { requirement => `user grier ryan', method => -1} ]

475 PerlAuthzHandler Once you get the Require directive back, you can decide which users meet the authorization requirement foreach my $requires { my = split " ", $requires->{requirement}; # We're ok if only valid-user was required. return OK if lc($directive) eq 'valid-user'; # Likewise if the user requirement was specified and # we match based on what we already know. return OK if lc($directive) eq 'user' && grep { $_ eq $r->user } [ { requirement => `user grier ryan', method => -1} ]

476 MIME-type Checking client request URI-based init URI translation
file-based init MIME setting resource control

477 MIME-type Checking We now have the physical resource and have decided the user can see it

478 MIME-type Checking We now have the physical resource and have decided the user can see it time to set the Content-Type header

479 MIME-type Checking We now have the physical resource and have decided the user can see it time to set the Content-Type header Apache's default is mod_mime, which examines the file extension

480 MIME-type Checking We now have the physical resource and have decided the user can see it time to set the Content-Type header Apache's default is mod_mime, which examines the file extension mod_mime also decides which content handler will run

481 MIME-type Checking We now have the physical resource and have decided the user can see it time to set the Content-Type header Apache's default is mod_mime, which examines the file extension mod_mime also decides which content handler will run AddHandler server-parsed

482 MIME-type Checking We now have the physical resource and have decided the user can see it time to set the Content-Type header Apache's default is mod_mime, which examines the file extension mod_mime also decides which content handler will run AddHandler server-parsed Winner takes all

483 MIME-type Checking client request URI-based init URI translation
file-based init MIME setting resource control

484 MIME-type Checking client request URI-based init URI translation
file-based init PerlTypeHandler resource control

485 PerlTypeHandler

486 PerlTypeHandler mod_mime has a stranglehold on the request

487 PerlTypeHandler mod_mime has a stranglehold on the request

488 PerlTypeHandler mod_mime has a stranglehold on the request
if you set the Content-Type and return OK, mod_mime won't set the content handler

489 PerlTypeHandler mod_mime has a stranglehold on the request
if you set the Content-Type and return OK, mod_mime won't set the content handler if you set the Content-Type and return DECLINED, mod_mime will clobber the Content-Type

490 PerlTypeHandler mod_mime has a stranglehold on the request
if you set the Content-Type and return OK, mod_mime won't set the content handler if you set the Content-Type and return DECLINED, mod_mime will clobber the Content-Type Best to just forget about the PerlTypeHandler

491 Fixups client request URI-based init URI translation fixups
file-based init MIME setting resource control

492 Fixups The final chance to fiddle with the request before content is written to the client

493 Fixups The final chance to fiddle with the request before content is written to the client non-specific phase

494 Fixups The final chance to fiddle with the request before content is written to the client non-specific phase Apache has no default behavior

495 Fixups The final chance to fiddle with the request before content is written to the client non-specific phase Apache has no default behavior All configured handlers will be run

496 Fixups client request URI-based init URI translation fixups
file-based init MIME setting resource control

497 Fixups client request URI-based init URI translation PerlFixupHandler
file-based init MIME setting resource control

498 PerlFixupHandler

499 PerlFixupHandler Good place to do anything you might have wanted to do in the PerlTypeHandler

500 PerlFixupHandler Good place to do anything you might have wanted to do in the PerlTypeHandler especially setting $r->handler()

501 Sample Fixup Object: re-implement XBitHack in Perl
Method: after some basic checks, turn the request over to mod_include using $r->handler()

502 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

503 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

504 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

505 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

506 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

507 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

508 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

509 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

510 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

511 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

512 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

513 package Cookbook::XBitHack;
use Apache::Constants qw(OK DECLINED OPT_INCLUDES); use Apache::File; use Fcntl qw(S_IXUSR S_IXGRP); use strict; sub handler { my $r = shift; return DECLINED unless (-f $r->finfo && # the file exists $r->content_type eq 'text/html' && # and is HTML $r->allow_options & OPT_INCLUDES); # and we have Options +Includes # Find out the user and group execution status. my $mode = (stat _)[2]; # We have to be user executable specifically. return DECLINED unless ($mode & S_IXUSR); # Set the Last-Modified header if group executable. $r->set_last_modified((stat _)[9]) if ($mode & S_IXGRP); # Make sure mod_include picks it up. $r->handler('server-parsed'); return OK; } 1;

514 Content Generation client request URI-based init content
URI translation fixups file-based init MIME setting resource control

515 Content Generation Apache's default content handler is default-handler, which takes care of all HTTP/1.1 events

516 Content Generation Apache's default content handler is default-handler, which takes care of all HTTP/1.1 events byteserving

517 Content Generation Apache's default content handler is default-handler, which takes care of all HTTP/1.1 events byteserving cache headers

518 Content Generation Apache's default content handler is default-handler, which takes care of all HTTP/1.1 events byteserving cache headers one and only one C module gets to handle content-generation

519 Content Generation Apache's default content handler is default-handler, which takes care of all HTTP/1.1 events byteserving cache headers one and only one C module gets to handle content-generation mod_cgi mod_perl mod_include

520 Content Generation client request URI-based init content
URI translation fixups file-based init MIME setting resource control

521 Content Generation client request URI-based init PerlHandler
URI translation fixups file-based init MIME setting resource control

522 Sample PerlHandler

523 Sample PerlHandler Object: save bandwidth universally

524 Sample PerlHandler Object: save bandwidth universally
Method: module HTML::Clean to "clean" outgoing documents

525 Sample PerlHandler Object: save bandwidth universally
Method: module HTML::Clean to "clean" outgoing documents Afterward: alter the handler to take advantage of more advanced mod_perl features

526 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

527 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

528 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

529 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

530 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

531 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

532 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

533 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

534 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

535 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

536 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

537 Configuration

538 Configuration add directives to httpd.conf to mirror DocumentRoot

539 Configuration add directives to httpd.conf to mirror DocumentRoot
Alias /clean /usr/local/apache/htdocs <Location /clean> SetHandler perl-script PerlHandler My::Clean </Location>

540 Results original: 202 bytes <html> <body>
<form method="GET" action="/foo"> Text: <input type="text" name="foo"><br> <input type="submit"> </form> <strong>hi there </strong> </body> </html>

541 Results original: 202 bytes clean: 145 bytes <html> <body>
<form method="GET" action="/foo"> Text: <input type="text" name="foo"><br> <input type="submit"> </form> <strong>hi there </strong> </body> </html> clean: 145 bytes <html><body><form method="GET" action="/foo"> Text: <input type="text" name="foo"><br><input type="submit"></form><b>hi there </b></body></html>

542 Results original: 202 bytes clean: 145 bytes <html> <body>
<form method="GET" action="/foo"> Text: <input type="text" name="foo"><br> <input type="submit"> </form> <strong>hi there </strong> </body> </html> clean: 145 bytes <html><body><form method="GET" action="/foo"> Text: <input type="text" name="foo"><br><input type="submit"></form><b>hi there </b></body></html>

543 Dynamic or Static?

544 Dynamic or Static? we often think of dynamic content as "could be different on any given access"

545 Dynamic or Static? we often think of dynamic content as "could be different on any given access" "dynamic" content can also be static with clearly defined factors that can change its meaning

546 Dynamic or Static? we often think of dynamic content as "could be different on any given access" "dynamic" content can also be static with clearly defined factors that can change its meaning by properly managing HTTP/1.1 cache headers, we can reduce strain on our servers

547 Conditional GET Request

548 Conditional GET Request
HTTP/1.1 allows for a conditional GET request

549 Conditional GET Request
HTTP/1.1 allows for a conditional GET request clients are allowed to use cached content based on information about the resource

550 Conditional GET Request
HTTP/1.1 allows for a conditional GET request clients are allowed to use cached content based on information about the resource information is provided by both the client and the server

551 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en]

552 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en]

553 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ OK Last-Modified: Thu, 01 Nov :35:27 GMT ETag: "4c be179cf" Accept-Ranges: bytes Content-Length: 9268 Connection: close Content-Type: text/html

554 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ OK Last-Modified: Thu, 01 Nov :35:27 GMT ETag: "4c be179cf" Accept-Ranges: bytes Content-Length: 9268 Connection: close Content-Type: text/html

555 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ OK Last-Modified: Thu, 01 Nov :35:27 GMT ETag: "4c be179cf" Accept-Ranges: bytes Content-Length: 9268 Connection: close Content-Type: text/html

556 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en]

557 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en]

558 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en]

559 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en]

560 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ Not Modified Last-Modified: Thu, 01 Nov :35:27 GMT ETag: "4c be179cf" Accept-Ranges: bytes Connection: close

561 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ Not Modified Last-Modified: Thu, 01 Nov :35:27 GMT ETag: "4c be179cf" Accept-Ranges: bytes Connection: close

562 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ Not Modified Last-Modified: Thu, 01 Nov :35:27 GMT ETag: "4c be179cf" Accept-Ranges: bytes Connection: close

563 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en]

564 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ OK Last-Modified: Thu, 06 Jun :51:11 GMT ETag: "4c cff4caf" Accept-Ranges: bytes Content-Length: 9268 Connection: close

565 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ OK Last-Modified: Thu, 06 Jun :51:11 GMT ETag: "4c cff4caf" Accept-Ranges: bytes Content-Length: 9268 Connection: close

566 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ OK Last-Modified: Thu, 06 Jun :51:11 GMT ETag: "4c cff4caf" Accept-Ranges: bytes Content-Length: 9268 Connection: close

567 GET /manual/index.html HTTP/1.1
Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Charset: windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso ;q=0.6, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Accept-Language: en Connection: Keep-Alive, TE Host: mainsheet.laserlink.net If-Modified-Since: Thu, 01 Nov :35:27 GMT If-None-Match: "4c be179cf TE: deflate, gzip, chunked, identity, trailers User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows XP) Opera [en] HTTP/ OK Last-Modified: Thu, 06 Jun :51:11 GMT ETag: "4c cff4caf" Accept-Ranges: bytes Content-Length: 9268 Connection: close

568 Conditional GET Request
for static documents, Apache takes care of making our response cache-friendly

569 Conditional GET Request
for static documents, Apache takes care of making our response cache-friendly since the file is on disk, Apache can determine when the file was last changed

570 Conditional GET Request
for static documents, Apache takes care of making our response cache-friendly since the file is on disk, Apache can determine when the file was last changed with static files, local modification is the only factor

571 Conditional GET Request
for static documents, Apache takes care of making our response cache-friendly since the file is on disk, Apache can determine when the file was last changed with static files, local modification is the only factor still too many rules to keep straight

572 Conditional GET Request
for static documents, Apache takes care of making our response cache-friendly since the file is on disk, Apache can determine when the file was last changed with static files, local modification is the only factor still too many rules to keep straight Apache provides an API to use so we don't have to think too much

573 Now for the Fun Part

574 Now for the Fun Part modify our PerlHandler to be "cache friendly"

575 Now for the Fun Part modify our PerlHandler to be "cache friendly"
send 304 when the document hasn't changed

576 Now for the Fun Part modify our PerlHandler to be "cache friendly"
send 304 when the document hasn't changed properly handle If-* header comparisons

577 How do you define change?

578 How do you define change?

579 How do you define change?
when dynamically altering static documents there are a number of factors to consider

580 How do you define change?
when dynamically altering static documents there are a number of factors to consider when the file changes on disk

581 How do you define change?
when dynamically altering static documents there are a number of factors to consider when the file changes on disk when the code changes

582 How do you define change?
when dynamically altering static documents there are a number of factors to consider when the file changes on disk when the code changes when the options to the code change

583 How do you define change?
when dynamically altering static documents there are a number of factors to consider when the file changes on disk when the code changes when the options to the code change all of these affect the "freshness" of the document

584 Code Changes

585 Code Changes in order to determine when the code itself changes, we need to mark the modification time of the package

586 Code Changes in order to determine when the code itself changes, we need to mark the modification time of the package at request time, we call an API to compare the package modification to the If-Modified-Since header

587 Code Changes in order to determine when the code itself changes, we need to mark the modification time of the package at request time, we call an API to compare the package modification to the If-Modified-Since header on reloads, we regenerate the package modification time

588 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; } 1;

589 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9];

590 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9];

591 Configuration Changes

592 Configuration Changes
in order to determine when the options to the code change, we need to mark the modification time of httpd.conf

593 Configuration Changes
in order to determine when the options to the code change, we need to mark the modification time of httpd.conf at request time, we call an API to compare the configuration modification to the If-Modified-Since header

594 Configuration Changes
in order to determine when the options to the code change, we need to mark the modification time of httpd.conf at request time, we call an API to compare the configuration modification to the If-Modified-Since header on restarts, we regenerate the configuration modification time

595 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9];

596 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9]; # ...and when httpd.conf was last modified my $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; # When the server is restarted we need to # make sure we recognize config file changes and propigate # them to the client to clear the client cache if necessary. Apache->server->register_cleanup(sub { $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; });

597 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9]; # ...and when httpd.conf was last modified my $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; # When the server is restarted we need to # make sure we recognize config file changes and propigate # them to the client to clear the client cache if necessary. Apache->server->register_cleanup(sub { $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; });

598 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9]; # ...and when httpd.conf was last modified my $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; # When the server is restarted we need to # make sure we recognize config file changes and propigate # them to the client to clear the client cache if necessary. Apache->server->register_cleanup(sub { $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; });

599 Resource Changes

600 Resource Changes in order to determine when the resources changes, we need to mark the modification time of $r->filename

601 Resource Changes in order to determine when the resources changes, we need to mark the modification time of $r->filename at request time, we call an API to compare the resource modification to the If-Modified-Since header

602 Resource Changes in order to determine when the resources changes, we need to mark the modification time of $r->filename at request time, we call an API to compare the resource modification to the If-Modified-Since header resource modification is checked on each request

603 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9]; # ...and when httpd.conf was last modified my $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; # When the server is restarted we need to # make sure we recognize config file changes and propigate # them to the client to clear the client cache if necessary. Apache->server->register_cleanup(sub { $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; });

604 use Apache::Constants qw(OK DECLINED); use Apache::File;
package My::Clean; use Apache::Constants qw(OK DECLINED); use Apache::File; use HTML::Clean; use strict; # Get the package modification time... (my $package = __PACKAGE__) =~ s!::!/!g; my $package_mtime = (stat $INC{"$package.pm"})[9]; # ...and when httpd.conf was last modified my $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; # When the server is restarted we need to # make sure we recognize config file changes and propigate # them to the client to clear the client cache if necessary. Apache->server->register_cleanup(sub { $conf_mtime = (stat Apache->server_root_relative('conf/httpd.conf'))[9]; }); sub handler { ... } 1;

605 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->send_http_header('text/html'); print ${$h->data}; return OK; }

606 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

607 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

608 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

609 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

610 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

611 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

612 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

613 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

614 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

615 my $fh = Apache::File->new($r->filename) or return DECLINED;
sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return DECLINED; my $dirty = do {local $/; <$fh>}; my $h = HTML::Clean->new(\$dirty); $h->level(3); $h->strip; $r->update_mtime($package_mtime); $r->update_mtime((stat $r->finfo)[9]); $r->update_mtime($conf_mtime); $r->set_last_modified; $r->set_etag; $r->set_content_length(length ${$h->data}); # only send the file if it meets cache criteria if ((my $status = $r->meets_conditions) == OK) { $r->send_http_header('text/html'); } else { return $status; print ${$h->data}; return OK;

616

617 Logging client request logging URI-based init content URI translation
fixups file-based init MIME setting resource control

618 Logging Apache's default is to use mod_log_config in common format

619 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

620 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

621 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

622 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 The connection to the client is still open!

623 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 The connection to the client is still open! All configured handlers run

624 Logging client request logging URI-based init content URI translation
fixups file-based init MIME setting resource control

625 Logging client request PerlLogHandler URI-based init content
URI translation fixups file-based init MIME setting resource control

626 PerlLogHandler

627 PerlLogHandler Useful for logging using interfaces in which Perl shines

628 PerlLogHandler Useful for logging using interfaces in which Perl shines like databases

629 Logging to a Database

630 Logging to a Database Logging directly to a database makes life easier if you have an application for which you need lots of reports

631 Logging to a Database Logging directly to a database makes life easier if you have an application for which you need lots of reports DBI rules

632 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 => $r->status, bytes => $r->bytes_sent, language => $r->headers_in->get('Accept-Language'), ); my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1;

633 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 => $r->status, bytes => $r->bytes_sent, language => $r->headers_in->get('Accept-Language'), ); my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1;

634 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 => $r->status, bytes => $r->bytes_sent, language => $r->headers_in->get('Accept-Language'), ); my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1;

635 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 => $r->status, bytes => $r->bytes_sent, language => $r->headers_in->get('Accept-Language'), ); my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1;

636 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 => $r->status, bytes => $r->bytes_sent, language => $r->headers_in->get('Accept-Language'), ); my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1;

637 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 => $r->status, bytes => $r->bytes_sent, language => $r->headers_in->get('Accept-Language'), ); my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1;

638 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 => $r->status, bytes => $r->bytes_sent, language => $r->headers_in->get('Accept-Language'), ); my $fields = join "$_,", keys %columns; my $values = join ', ', ('?') x values %columns; my $sql = qq( insert into (hit, servedate, $fields) values (hitsequence.nextval, sysdate, $values) my $sth = $dbh->prepare($sql); $sth->execute(values %columns); return OK; } 1;

639 Cleanups client request cleanups URI-based init logging content
URI translation fixups file-based init MIME setting resource control

640 Cleanups Apache doesn't really have a cleanup phase

641 Cleanups Apache doesn't really have a cleanup phase
It calls a function when the request memory pool is destroyed

642 Cleanups Apache doesn't really have a cleanup phase
It calls a function when the request memory pool is destroyed The connection to the client is closed

643 Cleanups client request cleanups URI-based init logging content
URI translation fixups file-based init MIME setting resource control

644 Cleanups client request PerlCleanupHandler URI-based init logging
content URI translation fixups file-based init MIME setting resource control

645 PerlCleanupHandler

646 PerlCleanupHandler Generally used to do any end of request cleanups

647 PerlCleanupHandler Generally used to do any end of request cleanups
Apache::File::tmpfile() removes its temporary file here

648 PerlCleanupHandler Generally used to do any end of request cleanups
Apache::File::tmpfile() removes its temporary file here Also good for logging

649 PerlCleanupHandler Generally used to do any end of request cleanups
Apache::File::tmpfile() removes its temporary file here Also good for logging no active browsers

650 Debugging

651 Debugging Let's examine a very conceptual debugging cleanup handler

652 Debugging Let's examine a very conceptual debugging cleanup handler
I actually did use it for a while

653 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

654 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

655 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

656 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

657 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

658 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

659 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

660 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

661 package Cookbook::TraceError;
use Apache::Constants qw(OK SERVER_ERROR DECLINED); use Apache::Log; use strict; sub handler { my $r = shift; # Don't do anything unless the main process errors. return DECLINED unless $r->is_initial_req && $r->status == SERVER_ERROR; my $old_loglevel = $r->server->loglevel(Apache::Log::DEBUG); my $old_trace = DBI->trace(2); # Start the debuggging request. my $sub = $r->lookup_uri($r->uri); # run() would ordinarily send content to the client, but # since we're in cleanup, the connection is already closed. $sub->run; # Reset things back to their original state - # loglevel(N) will persist for the lifetime of the child process. DBI->trace($old_trace); $r->server->loglevel($old_loglevel); return OK; } 1;

662

663 Fine Manuals Writing Apache Modules with Perl and C
mod_perl Developer's Cookbook mod_perl Pocket Reference mod_perl Guide mod_perl at the ASF

664 Materials These slides My modules
My modules

665


Download ppt "Programming the Apache Lifecycle"

Similar presentations


Ads by Google