ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/apache2-frontend/proxy_impl.pm
Revision: 1.4
Committed: Thu Jun 18 12:45:33 2015 UTC (8 years, 11 months ago) by root
Branch: MAIN
Changes since 1.3: +44 -6 lines
Log Message:
*** empty log message ***

File Contents

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