Start re-arranging and fixing docs. remove docs for deprecated stuff
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Engine.pm
CommitLineData
fc7ec1d9 1package Catalyst::Engine;
2
7fa2c9c1 3use Moose;
4with 'MooseX::Emulate::Class::Accessor::Fast';
5
fa32ac82 6use CGI::Simple::Cookie;
f63c03e4 7use Data::Dump qw/dump/;
d04b2ffd 8use Errno 'EWOULDBLOCK';
fc7ec1d9 9use HTML::Entities;
fbcc39ad 10use HTTP::Body;
fc7ec1d9 11use HTTP::Headers;
e0616220 12use URI::QueryParam;
a1791811 13use Plack::Loader;
b1ededd4 14use Catalyst::EngineLoader;
361ba9b2 15use Encode ();
16use utf8;
fbcc39ad 17
d495753a 18use namespace::clean -except => 'meta';
19
faa02805 20# Amount of data to read from input on each pass
21our $CHUNKSIZE = 64 * 1024;
a50e5b46 22
faa02805 23# XXX - this is only here for compat, do not use!
24has env => ( is => 'rw', writer => '_set_env' );
0c6352ff 25my $WARN_ABOUT_ENV = 0;
26around env => sub {
27 my ($orig, $self, @args) = @_;
28 if(@args) {
29 warn "env as a writer is deprecated, you probably need to upgrade Catalyst::Engine::PSGI"
30 unless $WARN_ABOUT_ENV++;
31 return $self->_set_env(@args);
32 }
33 return $self->$orig;
34};
35
ddcd2fc4 36# XXX - Only here for Engine::PSGI compat
37sub prepare_connection {
38 my ($self, $ctx) = @_;
39 $ctx->request->prepare_connection;
40}
41
fc7ec1d9 42=head1 NAME
43
44Catalyst::Engine - The Catalyst Engine
45
46=head1 SYNOPSIS
47
48See L<Catalyst>.
49
50=head1 DESCRIPTION
51
23f9d934 52=head1 METHODS
fc7ec1d9 53
cd3bb248 54
b5ecfcf0 55=head2 $self->finalize_body($c)
06e1b616 56
fbcc39ad 57Finalize body. Prints the response output.
06e1b616 58
59=cut
60
fbcc39ad 61sub finalize_body {
62 my ( $self, $c ) = @_;
7257e9db 63 my $body = $c->response->body;
f9b6d612 64 no warnings 'uninitialized';
7e95ba12 65 if ( blessed($body) && $body->can('read') or ref($body) eq 'GLOB' ) {
be1c9503 66 my $got;
67 do {
1235b30f 68 $got = read $body, my ($buffer), $CHUNKSIZE;
3a64ecc9 69 $got = 0 unless $self->write( $c, $buffer );
be1c9503 70 } while $got > 0;
71
7257e9db 72 close $body;
f4a57de4 73 }
74 else {
7257e9db 75 $self->write( $c, $body );
f4a57de4 76 }
ca3023fc 77
faa02805 78 my $res = $c->response;
79 $res->_writer->close;
80 $res->_clear_writer;
030674d0 81
ca3023fc 82 return;
fbcc39ad 83}
6dc87a0f 84
b5ecfcf0 85=head2 $self->finalize_cookies($c)
6dc87a0f 86
fa32ac82 87Create CGI::Simple::Cookie objects from $c->res->cookies, and set them as
88response headers.
4ab87e27 89
6dc87a0f 90=cut
91
92sub finalize_cookies {
fbcc39ad 93 my ( $self, $c ) = @_;
6dc87a0f 94
fbcc39ad 95 my @cookies;
7fa2c9c1 96 my $response = $c->response;
c82ed742 97
91772de9 98 foreach my $name (keys %{ $response->cookies }) {
99
100 my $val = $response->cookies->{$name};
fbcc39ad 101
2832cb5d 102 my $cookie = (
7e95ba12 103 blessed($val)
2832cb5d 104 ? $val
105 : CGI::Simple::Cookie->new(
106 -name => $name,
107 -value => $val->{value},
108 -expires => $val->{expires},
109 -domain => $val->{domain},
110 -path => $val->{path},
b21bc468 111 -secure => $val->{secure} || 0,
112 -httponly => $val->{httponly} || 0,
2832cb5d 113 )
6dc87a0f 114 );
0f12bef2 115 if (!defined $cookie) {
116 $c->log->warn("undef passed in '$name' cookie value - not setting cookie")
117 if $c->debug;
118 next;
119 }
6dc87a0f 120
fbcc39ad 121 push @cookies, $cookie->as_string;
6dc87a0f 122 }
6dc87a0f 123
b39840da 124 for my $cookie (@cookies) {
7fa2c9c1 125 $response->headers->push_header( 'Set-Cookie' => $cookie );
fbcc39ad 126 }
127}
969647fd 128
b5ecfcf0 129=head2 $self->finalize_error($c)
969647fd 130
6e5b548e 131Output an appropriate error message. Called if there's an error in $c
4ab87e27 132after the dispatch has finished. Will output debug messages if Catalyst
133is in debug mode, or a `please come back later` message otherwise.
134
969647fd 135=cut
136
c96cdcef 137sub _dump_error_page_element {
138 my ($self, $i, $element) = @_;
139 my ($name, $val) = @{ $element };
140
141 # This is fugly, but the metaclass is _HUGE_ and demands waaay too much
142 # scrolling. Suggestions for more pleasant ways to do this welcome.
143 local $val->{'__MOP__'} = "Stringified: "
1565e158 144 . $val->{'__MOP__'} if ref $val eq 'HASH' && exists $val->{'__MOP__'};
c96cdcef 145
146 my $text = encode_entities( dump( $val ));
147 sprintf <<"EOF", $name, $text;
148<h2><a href="#" onclick="toggleDump('dump_$i'); return false">%s</a></h2>
149<div id="dump_$i">
150 <pre wrap="">%s</pre>
151</div>
152EOF
153}
154
969647fd 155sub finalize_error {
fbcc39ad 156 my ( $self, $c ) = @_;
969647fd 157
7299a7b4 158 $c->res->content_type('text/html; charset=utf-8');
df960201 159 my $name = ref($c)->config->{name} || join(' ', split('::', ref $c));
361ba9b2 160
161 # Prevent Catalyst::Plugin::Unicode::Encoding from running.
162 # This is a little nasty, but it's the best way to be clean whether or
163 # not the user has an encoding plugin.
164
165 if ($c->can('encoding')) {
166 $c->{encoding} = '';
167 }
969647fd 168
169 my ( $title, $error, $infos );
170 if ( $c->debug ) {
62d9b030 171
172 # For pretty dumps
b5ecfcf0 173 $error = join '', map {
174 '<p><code class="error">'
175 . encode_entities($_)
176 . '</code></p>'
177 } @{ $c->error };
969647fd 178 $error ||= 'No output';
2666dd3b 179 $error = qq{<pre wrap="">$error</pre>};
969647fd 180 $title = $name = "$name on Catalyst $Catalyst::VERSION";
d82cc9ae 181 $name = "<h1>$name</h1>";
fbcc39ad 182
183 # Don't show context in the dump
02570318 184 $c->req->_clear_context;
185 $c->res->_clear_context;
fbcc39ad 186
187 # Don't show body parser in the dump
0f56bbcf 188 $c->req->_clear_body;
fbcc39ad 189
c6ef5e69 190 my @infos;
191 my $i = 0;
c6ef5e69 192 for my $dump ( $c->dump_these ) {
c96cdcef 193 push @infos, $self->_dump_error_page_element($i, $dump);
c6ef5e69 194 $i++;
195 }
196 $infos = join "\n", @infos;
969647fd 197 }
198 else {
199 $title = $name;
200 $error = '';
201 $infos = <<"";
202<pre>
203(en) Please come back later
0c2b4ac0 204(fr) SVP veuillez revenir plus tard
969647fd 205(de) Bitte versuchen sie es spaeter nocheinmal
d82cc9ae 206(at) Konnten's bitt'schoen spaeter nochmal reinschauen
969647fd 207(no) Vennligst prov igjen senere
d82cc9ae 208(dk) Venligst prov igen senere
209(pl) Prosze sprobowac pozniej
2f381252 210(pt) Por favor volte mais tarde
b31c0f2e 211(ru) Попробуйте еще раз позже
212(ua) Спробуйте ще раз пізніше
969647fd 213</pre>
214
215 $name = '';
216 }
e060fe05 217 $c->res->body( <<"" );
7299a7b4 218<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
219 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
220<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
969647fd 221<head>
7299a7b4 222 <meta http-equiv="Content-Language" content="en" />
223 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
969647fd 224 <title>$title</title>
7299a7b4 225 <script type="text/javascript">
c6ef5e69 226 <!--
227 function toggleDump (dumpElement) {
7299a7b4 228 var e = document.getElementById( dumpElement );
229 if (e.style.display == "none") {
230 e.style.display = "";
c6ef5e69 231 }
232 else {
7299a7b4 233 e.style.display = "none";
c6ef5e69 234 }
235 }
236 -->
237 </script>
969647fd 238 <style type="text/css">
239 body {
240 font-family: "Bitstream Vera Sans", "Trebuchet MS", Verdana,
241 Tahoma, Arial, helvetica, sans-serif;
34d28dfd 242 color: #333;
969647fd 243 background-color: #eee;
244 margin: 0px;
245 padding: 0px;
246 }
c6ef5e69 247 :link, :link:hover, :visited, :visited:hover {
34d28dfd 248 color: #000;
c6ef5e69 249 }
969647fd 250 div.box {
9619f23c 251 position: relative;
969647fd 252 background-color: #ccc;
253 border: 1px solid #aaa;
254 padding: 4px;
255 margin: 10px;
969647fd 256 }
257 div.error {
34d28dfd 258 background-color: #cce;
969647fd 259 border: 1px solid #755;
260 padding: 8px;
261 margin: 4px;
262 margin-bottom: 10px;
969647fd 263 }
264 div.infos {
34d28dfd 265 background-color: #eee;
969647fd 266 border: 1px solid #575;
267 padding: 8px;
268 margin: 4px;
269 margin-bottom: 10px;
969647fd 270 }
271 div.name {
34d28dfd 272 background-color: #cce;
969647fd 273 border: 1px solid #557;
274 padding: 8px;
275 margin: 4px;
969647fd 276 }
7f8e0078 277 code.error {
278 display: block;
279 margin: 1em 0;
280 overflow: auto;
7f8e0078 281 }
9619f23c 282 div.name h1, div.error p {
283 margin: 0;
284 }
285 h2 {
286 margin-top: 0;
287 margin-bottom: 10px;
288 font-size: medium;
289 font-weight: bold;
290 text-decoration: underline;
291 }
292 h1 {
293 font-size: medium;
294 font-weight: normal;
295 }
2666dd3b 296 /* from http://users.tkk.fi/~tkarvine/linux/doc/pre-wrap/pre-wrap-css3-mozilla-opera-ie.html */
297 /* Browser specific (not valid) styles to make preformatted text wrap */
b0ad47c1 298 pre {
2666dd3b 299 white-space: pre-wrap; /* css-3 */
300 white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
301 white-space: -pre-wrap; /* Opera 4-6 */
302 white-space: -o-pre-wrap; /* Opera 7 */
303 word-wrap: break-word; /* Internet Explorer 5.5+ */
304 }
969647fd 305 </style>
306</head>
307<body>
308 <div class="box">
309 <div class="error">$error</div>
310 <div class="infos">$infos</div>
311 <div class="name">$name</div>
312 </div>
313</body>
314</html>
315
4b66aa19 316 # Trick IE. Old versions of IE would display their own error page instead
317 # of ours if we'd give it less than 512 bytes.
d82cc9ae 318 $c->res->{body} .= ( ' ' x 512 );
319
361ba9b2 320 $c->res->{body} = Encode::encode("UTF-8", $c->res->{body});
321
d82cc9ae 322 # Return 500
33117422 323 $c->res->status(500);
969647fd 324}
325
b5ecfcf0 326=head2 $self->finalize_headers($c)
fc7ec1d9 327
9c4288ea 328Allows engines to write headers to response
4ab87e27 329
fc7ec1d9 330=cut
331
44d28c7d 332sub finalize_headers {
333 my ($self, $ctx) = @_;
334
9c4288ea 335 $ctx->response->finalize_headers;
44d28c7d 336 return;
337}
fc7ec1d9 338
b5ecfcf0 339=head2 $self->finalize_uploads($c)
fc7ec1d9 340
4ab87e27 341Clean up after uploads, deleting temp files.
342
fc7ec1d9 343=cut
344
fbcc39ad 345sub finalize_uploads {
346 my ( $self, $c ) = @_;
99fe1710 347
671123ba 348 # N.B. This code is theoretically entirely unneeded due to ->cleanup(1)
349 # on the HTTP::Body object.
7fa2c9c1 350 my $request = $c->request;
91772de9 351 foreach my $key (keys %{ $request->uploads }) {
352 my $upload = $request->uploads->{$key};
7fa2c9c1 353 unlink grep { -e $_ } map { $_->tempname }
354 (ref $upload eq 'ARRAY' ? @{$upload} : ($upload));
c85ff642 355 }
7fa2c9c1 356
fc7ec1d9 357}
358
b5ecfcf0 359=head2 $self->prepare_body($c)
fc7ec1d9 360
4ab87e27 361sets up the L<Catalyst::Request> object body using L<HTTP::Body>
362
fc7ec1d9 363=cut
364
fbcc39ad 365sub prepare_body {
366 my ( $self, $c ) = @_;
99fe1710 367
398f13db 368 $c->request->prepare_body;
fc7ec1d9 369}
370
b5ecfcf0 371=head2 $self->prepare_body_chunk($c)
4bd82c41 372
4ab87e27 373Add a chunk to the request body.
374
4bd82c41 375=cut
376
398f13db 377# XXX - Can this be deleted?
4bd82c41 378sub prepare_body_chunk {
379 my ( $self, $c, $chunk ) = @_;
4f5ebacd 380
398f13db 381 $c->request->prepare_body_chunk($chunk);
4bd82c41 382}
383
b5ecfcf0 384=head2 $self->prepare_body_parameters($c)
06e1b616 385
b0ad47c1 386Sets up parameters from body.
4ab87e27 387
06e1b616 388=cut
389
fbcc39ad 390sub prepare_body_parameters {
391 my ( $self, $c ) = @_;
b0ad47c1 392
398f13db 393 $c->request->prepare_body_parameters;
fbcc39ad 394}
0556eb49 395
b5ecfcf0 396=head2 $self->prepare_parameters($c)
fc7ec1d9 397
4ab87e27 398sets up parameters from query and post parameters.
399
fc7ec1d9 400=cut
401
fbcc39ad 402sub prepare_parameters {
403 my ( $self, $c ) = @_;
fc7ec1d9 404
1cbdfa9b 405 $c->request->parameters;
fbcc39ad 406}
407
b5ecfcf0 408=head2 $self->prepare_path($c)
fc7ec1d9 409
4ab87e27 410abstract method, implemented by engines.
411
fc7ec1d9 412=cut
413
44d28c7d 414sub prepare_path {
415 my ($self, $ctx) = @_;
416
faa02805 417 my $env = $ctx->request->env;
44d28c7d 418
419 my $scheme = $ctx->request->secure ? 'https' : 'http';
420 my $host = $env->{HTTP_HOST} || $env->{SERVER_NAME};
421 my $port = $env->{SERVER_PORT} || 80;
422 my $base_path = $env->{SCRIPT_NAME} || "/";
423
424 # set the request URI
661de072 425 my $path;
426 if (!$ctx->config->{use_request_uri_for_path}) {
4904ee27 427 my $path_info = $env->{PATH_INFO};
428 if ( exists $env->{REDIRECT_URL} ) {
429 $base_path = $env->{REDIRECT_URL};
430 $base_path =~ s/\Q$path_info\E$//;
431 }
432 $path = $base_path . $path_info;
661de072 433 $path =~ s{^/+}{};
434 $path =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
435 $path =~ s/\?/%3F/g; # STUPID STUPID SPECIAL CASE
436 }
437 else {
438 my $req_uri = $env->{REQUEST_URI};
439 $req_uri =~ s/\?.*$//;
440 $path = $req_uri;
441 $path =~ s{^/+}{};
442 }
44d28c7d 443
444 # Using URI directly is way too slow, so we construct the URLs manually
445 my $uri_class = "URI::$scheme";
446
447 # HTTP_HOST will include the port even if it's 80/443
448 $host =~ s/:(?:80|443)$//;
449
450 if ($port !~ /^(?:80|443)$/ && $host !~ /:/) {
451 $host .= ":$port";
452 }
453
44d28c7d 454 my $query = $env->{QUERY_STRING} ? '?' . $env->{QUERY_STRING} : '';
455 my $uri = $scheme . '://' . $host . '/' . $path . $query;
456
4ee03d72 457 $ctx->request->uri( (bless \$uri, $uri_class)->canonical );
44d28c7d 458
459 # set the base URI
460 # base must end in a slash
461 $base_path .= '/' unless $base_path =~ m{/$};
462
463 my $base_uri = $scheme . '://' . $host . $base_path;
464
465 $ctx->request->base( bless \$base_uri, $uri_class );
466
467 return;
468}
fc7ec1d9 469
b5ecfcf0 470=head2 $self->prepare_request($c)
fc7ec1d9 471
b5ecfcf0 472=head2 $self->prepare_query_parameters($c)
fc7ec1d9 473
4ab87e27 474process the query string and extract query parameters.
475
fc7ec1d9 476=cut
477
e0616220 478sub prepare_query_parameters {
44d28c7d 479 my ($self, $c) = @_;
480
faa02805 481 my $env = $c->request->env;
482 my $query_string = exists $env->{QUERY_STRING}
483 ? $env->{QUERY_STRING}
44d28c7d 484 : '';
b0ad47c1 485
3b4d1251 486 # Check for keywords (no = signs)
487 # (yes, index() is faster than a regex :))
933ba403 488 if ( index( $query_string, '=' ) < 0 ) {
3b4d1251 489 $c->request->query_keywords( $self->unescape_uri($query_string) );
933ba403 490 return;
491 }
492
493 my %query;
e0616220 494
495 # replace semi-colons
496 $query_string =~ s/;/&/g;
b0ad47c1 497
2f381252 498 my @params = grep { length $_ } split /&/, $query_string;
e0616220 499
933ba403 500 for my $item ( @params ) {
b0ad47c1 501
502 my ($param, $value)
933ba403 503 = map { $self->unescape_uri($_) }
e5542b70 504 split( /=/, $item, 2 );
b0ad47c1 505
933ba403 506 $param = $self->unescape_uri($item) unless defined $param;
b0ad47c1 507
933ba403 508 if ( exists $query{$param} ) {
509 if ( ref $query{$param} ) {
510 push @{ $query{$param} }, $value;
511 }
512 else {
513 $query{$param} = [ $query{$param}, $value ];
514 }
515 }
516 else {
517 $query{$param} = $value;
518 }
e0616220 519 }
933ba403 520 $c->request->query_parameters( \%query );
e0616220 521}
fbcc39ad 522
b5ecfcf0 523=head2 $self->prepare_read($c)
fbcc39ad 524
4ab87e27 525prepare to read from the engine.
526
fbcc39ad 527=cut
fc7ec1d9 528
fbcc39ad 529sub prepare_read {
530 my ( $self, $c ) = @_;
4f5ebacd 531
878b821c 532 # Initialize the amount of data we think we need to read
faa02805 533 $c->request->_read_length;
fbcc39ad 534}
fc7ec1d9 535
b5ecfcf0 536=head2 $self->prepare_request(@arguments)
fc7ec1d9 537
4ab87e27 538Populate the context object from the request object.
539
fc7ec1d9 540=cut
541
44d28c7d 542sub prepare_request {
543 my ($self, $ctx, %args) = @_;
faa02805 544 $ctx->request->_set_env($args{env});
545 $self->_set_env($args{env}); # Nasty back compat!
546 $ctx->response->_set_response_cb($args{response_cb});
44d28c7d 547}
fc7ec1d9 548
b5ecfcf0 549=head2 $self->prepare_uploads($c)
c9afa5fc 550
fbcc39ad 551=cut
552
553sub prepare_uploads {
554 my ( $self, $c ) = @_;
7fa2c9c1 555
556 my $request = $c->request;
0f56bbcf 557 return unless $request->_body;
7fa2c9c1 558
0f56bbcf 559 my $uploads = $request->_body->upload;
7fa2c9c1 560 my $parameters = $request->parameters;
91772de9 561 foreach my $name (keys %$uploads) {
562 my $files = $uploads->{$name};
fbcc39ad 563 my @uploads;
7fa2c9c1 564 for my $upload (ref $files eq 'ARRAY' ? @$files : ($files)) {
565 my $headers = HTTP::Headers->new( %{ $upload->{headers} } );
566 my $u = Catalyst::Request::Upload->new
567 (
568 size => $upload->{size},
a160c98d 569 type => scalar $headers->content_type,
7fa2c9c1 570 headers => $headers,
571 tempname => $upload->{tempname},
572 filename => $upload->{filename},
573 );
fbcc39ad 574 push @uploads, $u;
575 }
7fa2c9c1 576 $request->uploads->{$name} = @uploads > 1 ? \@uploads : $uploads[0];
f4a57de4 577
c4bed79a 578 # support access to the filename as a normal param
579 my @filenames = map { $_->{filename} } @uploads;
a7e05d9d 580 # append, if there's already params with this name
7fa2c9c1 581 if (exists $parameters->{$name}) {
582 if (ref $parameters->{$name} eq 'ARRAY') {
583 push @{ $parameters->{$name} }, @filenames;
a7e05d9d 584 }
585 else {
7fa2c9c1 586 $parameters->{$name} = [ $parameters->{$name}, @filenames ];
a7e05d9d 587 }
588 }
589 else {
7fa2c9c1 590 $parameters->{$name} = @filenames > 1 ? \@filenames : $filenames[0];
a7e05d9d 591 }
fbcc39ad 592 }
593}
594
767480fd 595=head2 $self->write($c, $buffer)
596
597Writes the buffer to the client.
598
599=cut
600
601sub write {
602 my ( $self, $c, $buffer ) = @_;
603
604 $c->response->write($buffer);
605}
606
b5ecfcf0 607=head2 $self->read($c, [$maxlength])
fbcc39ad 608
ea72fece 609Reads from the input stream by calling C<< $self->read_chunk >>.
610
611Maintains the read_length and read_position counters as data is read.
612
fbcc39ad 613=cut
614
615sub read {
616 my ( $self, $c, $maxlength ) = @_;
4f5ebacd 617
f083854e 618 $c->request->read($maxlength);
fbcc39ad 619}
fc7ec1d9 620
87f50436 621=head2 $self->read_chunk($c, \$buffer, $length)
23f9d934 622
10011c19 623Each engine implements read_chunk as its preferred way of reading a chunk
ea72fece 624of data. Returns the number of bytes read. A return of 0 indicates that
625there is no more data to be read.
fc7ec1d9 626
fbcc39ad 627=cut
61b1e958 628
e6b46d80 629sub read_chunk {
ce7abbda 630 my ($self, $ctx) = (shift, shift);
87f50436 631 return $ctx->request->read_chunk(@_);
e6b46d80 632}
61b1e958 633
9560b708 634=head2 $self->run($app, $server)
63b763c5 635
9560b708 636Start the engine. Builds a PSGI application and calls the
acbecf08 637run method on the server passed in, which then causes the
638engine to loop, handling requests..
4ab87e27 639
fbcc39ad 640=cut
fc7ec1d9 641
44d28c7d 642sub run {
51857616 643 my ($self, $app, $psgi, @args) = @_;
acbecf08 644 # @args left here rather than just a $options, $server for back compat with the
645 # old style scripts which send a few args, then a hashref
646
647 # They should never actually be used in the normal case as the Plack engine is
648 # passed in got all the 'standard' args via the loader in the script already.
649
650 # FIXME - we should stash the options in an attribute so that custom args
651 # like Gitalist's --git_dir are possible to get from the app without stupid tricks.
1e5dad00 652 my $server = pop @args if (scalar @args && blessed $args[-1]);
653 my $options = pop @args if (scalar @args && ref($args[-1]) eq 'HASH');
ccb13b15 654 # Back compat hack for applications with old (non Catalyst::Script) scripts to work in FCGI.
655 if (scalar @args && !ref($args[0])) {
656 if (my $listen = shift @args) {
657 $options->{listen} ||= [$listen];
658 }
659 }
acbecf08 660 if (! $server ) {
f7a3f8fd 661 $server = Catalyst::EngineLoader->new(application_name => ref($self))->auto(%$options);
f7f55b2f 662 # We're not being called from a script, so auto detect what backend to
663 # run on. This should never happen, as mod_perl never calls ->run,
664 # instead the $app->handle method is called per request.
acbecf08 665 $app->log->warn("Not supplied a Plack engine, falling back to engine auto-loader (are your scripts ancient?)")
666 }
aee7cdcc 667 $app->run_options($options);
acbecf08 668 $server->run($psgi, $options);
a1791811 669}
44d28c7d 670
9560b708 671=head2 build_psgi_app ($app, @args)
672
673Builds and returns a PSGI application closure, wrapping it in the reverse proxy
674middleware if the using_frontend_proxy config setting is set.
675
676=cut
677
22a5833d 678sub build_psgi_app {
a1791811 679 my ($self, $app, @args) = @_;
c2f4a965 680
fcffcb05 681 return sub {
44d28c7d 682 my ($env) = @_;
683
684 return sub {
685 my ($respond) = @_;
faa02805 686 $app->handle_request(env => $env, response_cb => $respond);
44d28c7d 687 };
688 };
689}
fc7ec1d9 690
933ba403 691=head2 $self->unescape_uri($uri)
692
6a44fe01 693Unescapes a given URI using the most efficient method available. Engines such
694as Apache may implement this using Apache's C-based modules, for example.
933ba403 695
696=cut
697
698sub unescape_uri {
8c7d83e1 699 my ( $self, $str ) = @_;
7d22a537 700
701 $str =~ s/(?:%([0-9A-Fa-f]{2})|\+)/defined $1 ? chr(hex($1)) : ' '/eg;
702
8c7d83e1 703 return $str;
933ba403 704}
34d28dfd 705
4ab87e27 706=head2 $self->finalize_output
707
708<obsolete>, see finalize_body
709
0c76ec45 710=head2 $self->env
711
6356febf 712Hash containing environment variables including many special variables inserted
0c76ec45 713by WWW server - like SERVER_*, REMOTE_*, HTTP_* ...
714
6356febf 715Before accessing environment variables consider whether the same information is
0c76ec45 716not directly available via Catalyst objects $c->request, $c->engine ...
717
6356febf 718BEWARE: If you really need to access some environment variable from your Catalyst
0c76ec45 719application you should use $c->engine->env->{VARNAME} instead of $ENV{VARNAME},
ae7da8f5 720as in some environments the %ENV hash does not contain what you would expect.
0c76ec45 721
fbcc39ad 722=head1 AUTHORS
723
2f381252 724Catalyst Contributors, see Catalyst.pm
fc7ec1d9 725
726=head1 COPYRIGHT
727
536bee89 728This library is free software. You can redistribute it and/or modify it under
fc7ec1d9 729the same terms as Perl itself.
730
731=cut
732
7331;