ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/apache2-frontend/proxy_impl.pm
Revision: 1.3
Committed: Tue Jun 16 12:52:13 2015 UTC (11 years, 3 months ago) by root
Branch: MAIN
Changes since 1.2: +11 -7 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 package proxy_impl;
2    
3     use overload;
4    
5 root 1.2 # regex syntax
6     # . literal dot (like "\." in normaql regexes)
7     # · match one character (like "." in normal regexes)
8     # ** match anything (like ".*" in normal regexes)
9 root 1.1 sub uriregex {
10     overload::constant qr => sub {
11     local $_ = shift;
12    
13     s/\./\\./g;
14     s/·/./g;
15     s/\*\*/.*/g;
16    
17     $_
18     };
19     }
20    
21     BEGIN { uriregex }
22    
23     use Apache2::Const -compile => qw(
24     OK DECLINED DONE NOT_FOUND SERVER_ERROR AUTH_REQUIRED FORBIDDEN
25     PROXYREQ_REVERSE
26     );
27     use Apache2::RequestUtil ();
28     use Apache2::RequestRec ();
29     use Apache2::Connection ();
30     use Apache2::Util ();
31     use Apache2::URI ();
32    
33     use APR::Const -compile => qw(
34     FILETYPE_DIR FINFO_NORM
35     );
36     use APR::Error ();
37     use APR::Finfo ();
38    
39     use common::sense;
40    
41     # per-request values, cached here for performance, use them
42     our $req; # Apache2::RquestRec
43     our $host; # $req->hostname
44     our $uri; # $req->uri, already resolved .. and protects against %xx, hopefully
45     our $ip; # $req->connection->client_ip, alternative ->useragent_ip
46    
47     # finish immediately with given status
48     sub status($) {
49     die \(my $status = shift);
50     }
51    
52     #sub escape_path($) {
53     # Apache2::Util::escape_path shift, $req->pool
54     #}
55    
56     # escapes everything not allowed in a url hpath
57     sub pesc($) {
58     local $_ = shift;
59    
60     s/([^A-Za-z0-9\$\-_.+!*'(),;?&=]\/)/sprintf "%02x", ord $1/ge;
61    
62     $_
63     }
64    
65     # external redirect with status code
66     sub redirect($$) {
67     my ($status, $location) = @_;
68    
69     # $location =~ s,^(http://[^/:]+)/,$1:34567/,;#d#
70    
71     $req->headers_out->set (Location => $location);
72     status $status;
73     }
74    
75     # permanent external redirect
76     sub rperm($) {
77     redirect 301, shift
78     }
79    
80     # temporary redirect, could be internal, but never is
81     sub rtemp($) {
82     redirect 302, shift
83     }
84    
85     # serve some path, do not call directly
86     sub _rpathname($) {
87     my $path = shift;
88    
89     my $finfo = eval { APR::Finfo::stat $path, APR::Const::FINFO_NORM, $req->pool }
90     or status Apache2::Const::NOT_FOUND;
91    
92     # let mod_dir, mod_autoindex and the default-handler handle this
93     $req->filename ($path);
94     $req->finfo ($finfo);
95     # warn "serve <$path,",$req->handler,">\n";#d#
96     status Apache2::Const::OK;
97     }
98    
99     # serve a regular file from disk
100     sub rfile($) {
101     $req->handler ("default-handler");
102     &_rpathname
103     }
104    
105     # serve a directory from disk
106     sub rdir($) {
107     my $path = shift;
108    
109     if ($req->uri !~ m,/$,) {
110     # redirect dir to dir/
111     rperm $req->construct_url (pesc $req->uri . "/");
112    
113     } elsif (-e "$path/index.html") {
114     # mod_dir emulation, display index.html if any
115     $req->content_type ("text/html");
116     rfile "$path/index.html";
117     } elsif (-e "$path/index.xhtml") {
118     $req->content_type ("text/html"); # we assume to follow the compatibility guidelines
119     rfile "$path/index.xhtml";
120    
121     } else {
122     # let mod_autoindex handle it later
123     $req->handler ("httpd/unix-directory");
124     _rpathname $path;
125     }
126     }
127    
128     # like rpath, but assumes caller already stat'ed
129 root 1.2 sub rpath_nostat($) {
130 root 1.1 -d _ ? &rdir : &rfile
131     }
132    
133     # serve a generic path, can be dir or file
134     sub rpath($) {
135     stat $_[0];
136     &rpath_nostat
137     }
138    
139 root 1.3 # run a cgi script, first argument is script,
140     # $1 and $2 MUST be SCRIPT_NAME and PATH_INFO, respectively
141     sub rcgi($) {
142     my ($path) = @_;
143 root 1.1
144 root 1.3 $req->uri ("$1$2");
145     $req->path_info ("$2");
146 root 1.1
147     $req->handler ("cgi-script");
148     $req->notes->set ("alias-forced-type" => "cgi-script"); # leave a note for mod_cgi, so it ignores missing ExecCGI
149     _rpathname $path;
150     }
151    
152 root 1.2 # simplest reverse proxy, target is target url for this request
153     sub rproxy($) {
154     my ($target) = @_;
155 root 1.1
156     $req->proxyreq (Apache2::Const::PROXYREQ_REVERSE);
157 root 1.2 $req->filename ("proxy:$target");
158 root 1.1 $req->handler ("proxy-server");
159     $req->subprocess_env->set ("proxy-sendchunked", 1);
160     #$notes->set ("proxy-nocanon", 1);
161     #$env->set ("proxy-initial-not-pooled", 1);
162    
163     # disable compression, see http://www.apachetutor.org/admin/reverseproxies
164     # $req->headers_in->unset ("accept-encoding");
165     # alternatively recompress, SetOutputFilter INFLATE;DEFLATE
166    
167 root 1.2 status Apache2::Const::OK;
168     }
169    
170 root 1.3 # host, port
171     # $1 and $2 MUST be SCRIPT_NAME and PATH_INFO, respectively
172 root 1.2 sub rscgi($;$) {
173 root 1.3 my ($target) = @_;
174 root 1.2
175 root 1.3 $req->uri ("$1$2");
176     $req->path_info ("$2");
177 root 1.2 rproxy $target;
178    
179     # notes: uds_path
180     }
181    
182     # reverse proxy
183     # path is local root uri
184     # target is target root url
185     # suffix is appended to target url
186     # @lines is extra config lines
187     sub rproxy_html($$$@) {
188     my ($path, $target, $suffix, @lines) = @_;
189    
190 root 1.1 (my $cpath = $path) =~ s/([^A-Za-z0-9\/.\-_])/sprintf "\\x%02x", ord $1/ge;
191    
192     push @lines, (
193     "ProxyPassReverse /",
194     "ProxyHTMLEnable on",
195     "ProxyHTMLURLMap $target $cpath",
196     "ProxyHTMLURLMap / $cpath/",
197     );
198    
199     # warn "PROXY<$path,$target,$suffix>\n";#d#
200     # warn map "$_\n",@lines;
201    
202     $req->add_config (\@lines, ~0, $path);
203    
204 root 1.2 rproxy "$target$suffix";
205 root 1.1 }
206    
207     my $rules;
208    
209     sub load_rules {
210     open my $fh, "<:raw", Apache2::ServerUtil::server_root . "/rules"
211     or die Apache2::ServerUtil::server_root . "/rules: $!";
212     local $/;
213    
214     $rules = <$fh>;
215    
216 root 1.2 $rules = eval "use common::sense; BEGIN { uriregex }\nsub {\n#line 0 'rules'\n$rules\n}"
217 root 1.1 or die $@;
218    
219     warn "proxy rules successfully loaded.\n";
220    
221     Apache2::Const::OK
222     }
223    
224     sub map_to_storage {
225     local $req = shift;
226     local ($host, $uri, $ip) = ($req->hostname, $req->uri, $req->connection->client_ip);
227    
228     eval {
229     # the uri is pre-parsed and "protected" by apache
230     # the hostname is lowercased but otherwise completely unchecked,
231     # so better be safe than sorry
232     $host =~ /^[A-Za-z0-9\-.]+$/
233     or status 404;
234    
235     # must have at least one dot
236     $host =~ /./
237     or status 404;
238    
239     local $_ = "$host$uri";
240     $rules->($req);
241     };
242    
243     if ($@) {
244     if (SCALAR:: eq ref $@) {
245     return ${$@};
246     } else {
247     die;
248     }
249     }
250    
251     Apache2::Const::NOT_FOUND
252     }
253    
254     load_rules;
255    
256     warn "proxy loaded and iniitalised.\n";
257    
258     1
259