Download presentation
Presentation is loading. Please wait.
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
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
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
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
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
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;
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;
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
Similar presentations
© 2024 SlidePlayer.com Inc.
All rights reserved.