version bumping
[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}
4bd82c41 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
258733f1 183 # Don't show context in the dump
184 $c->res->_clear_context;
185
fbcc39ad 186 # Don't show body parser in the dump
0f56bbcf 187 $c->req->_clear_body;
fbcc39ad 188
c6ef5e69 189 my @infos;
190 my $i = 0;
c6ef5e69 191 for my $dump ( $c->dump_these ) {
c96cdcef 192 push @infos, $self->_dump_error_page_element($i, $dump);
c6ef5e69 193 $i++;
194 }
195 $infos = join "\n", @infos;
969647fd 196 }
197 else {
198 $title = $name;
199 $error = '';
200 $infos = <<"";
201<pre>
202(en) Please come back later
0c2b4ac0 203(fr) SVP veuillez revenir plus tard
969647fd 204(de) Bitte versuchen sie es spaeter nocheinmal
d82cc9ae 205(at) Konnten's bitt'schoen spaeter nochmal reinschauen
969647fd 206(no) Vennligst prov igjen senere
d82cc9ae 207(dk) Venligst prov igen senere
208(pl) Prosze sprobowac pozniej
2f381252 209(pt) Por favor volte mais tarde
b31c0f2e 210(ru) Попробуйте еще раз позже
211(ua) Спробуйте ще раз пізніше
969647fd 212</pre>
213
214 $name = '';
215 }
e060fe05 216 $c->res->body( <<"" );
7299a7b4 217<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
218 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
219<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
969647fd 220<head>
7299a7b4 221 <meta http-equiv="Content-Language" content="en" />
222 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
969647fd 223 <title>$title</title>
7299a7b4 224 <script type="text/javascript">
c6ef5e69 225 <!--
226 function toggleDump (dumpElement) {
7299a7b4 227 var e = document.getElementById( dumpElement );
228 if (e.style.display == "none") {
229 e.style.display = "";
c6ef5e69 230 }
231 else {
7299a7b4 232 e.style.display = "none";
c6ef5e69 233 }
234 }
235 -->
236 </script>
969647fd 237 <style type="text/css">
238 body {
239 font-family: "Bitstream Vera Sans", "Trebuchet MS", Verdana,
240 Tahoma, Arial, helvetica, sans-serif;
34d28dfd 241 color: #333;
969647fd 242 background-color: #eee;
243 margin: 0px;
244 padding: 0px;
245 }
c6ef5e69 246 :link, :link:hover, :visited, :visited:hover {
34d28dfd 247 color: #000;
c6ef5e69 248 }
969647fd 249 div.box {
9619f23c 250 position: relative;
969647fd 251 background-color: #ccc;
252 border: 1px solid #aaa;
253 padding: 4px;
254 margin: 10px;
969647fd 255 }
256 div.error {
34d28dfd 257 background-color: #cce;
969647fd 258 border: 1px solid #755;
259 padding: 8px;
260 margin: 4px;
261 margin-bottom: 10px;
969647fd 262 }
263 div.infos {
34d28dfd 264 background-color: #eee;
969647fd 265 border: 1px solid #575;
266 padding: 8px;
267 margin: 4px;
268 margin-bottom: 10px;
969647fd 269 }
270 div.name {
34d28dfd 271 background-color: #cce;
969647fd 272 border: 1px solid #557;
273 padding: 8px;
274 margin: 4px;
969647fd 275 }
7f8e0078 276 code.error {
277 display: block;
278 margin: 1em 0;
279 overflow: auto;
7f8e0078 280 }
9619f23c 281 div.name h1, div.error p {
282 margin: 0;
283 }
284 h2 {
285 margin-top: 0;
286 margin-bottom: 10px;
287 font-size: medium;
288 font-weight: bold;
289 text-decoration: underline;
290 }
291 h1 {
292 font-size: medium;
293 font-weight: normal;
294 }
2666dd3b 295 /* from http://users.tkk.fi/~tkarvine/linux/doc/pre-wrap/pre-wrap-css3-mozilla-opera-ie.html */
296 /* Browser specific (not valid) styles to make preformatted text wrap */
b0ad47c1 297 pre {
2666dd3b 298 white-space: pre-wrap; /* css-3 */
299 white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
300 white-space: -pre-wrap; /* Opera 4-6 */
301 white-space: -o-pre-wrap; /* Opera 7 */
302 word-wrap: break-word; /* Internet Explorer 5.5+ */
303 }
969647fd 304 </style>
305</head>
306<body>
307 <div class="box">
308 <div class="error">$error</div>
309 <div class="infos">$infos</div>
310 <div class="name">$name</div>
311 </div>
312</body>
313</html>
314
4b66aa19 315 # Trick IE. Old versions of IE would display their own error page instead
316 # of ours if we'd give it less than 512 bytes.
d82cc9ae 317 $c->res->{body} .= ( ' ' x 512 );
318
361ba9b2 319 $c->res->{body} = Encode::encode("UTF-8", $c->res->{body});
320
d82cc9ae 321 # Return 500
33117422 322 $c->res->status(500);
969647fd 323}
324
b5ecfcf0 325=head2 $self->finalize_headers($c)
fc7ec1d9 326
9c4288ea 327Allows engines to write headers to response
4ab87e27 328
fc7ec1d9 329=cut
330
44d28c7d 331sub finalize_headers {
332 my ($self, $ctx) = @_;
333
9c4288ea 334 $ctx->response->finalize_headers;
44d28c7d 335 return;
336}
fc7ec1d9 337
b5ecfcf0 338=head2 $self->finalize_uploads($c)
fc7ec1d9 339
4ab87e27 340Clean up after uploads, deleting temp files.
341
fc7ec1d9 342=cut
343
fbcc39ad 344sub finalize_uploads {
345 my ( $self, $c ) = @_;
99fe1710 346
671123ba 347 # N.B. This code is theoretically entirely unneeded due to ->cleanup(1)
348 # on the HTTP::Body object.
7fa2c9c1 349 my $request = $c->request;
91772de9 350 foreach my $key (keys %{ $request->uploads }) {
351 my $upload = $request->uploads->{$key};
7fa2c9c1 352 unlink grep { -e $_ } map { $_->tempname }
353 (ref $upload eq 'ARRAY' ? @{$upload} : ($upload));
c85ff642 354 }
7fa2c9c1 355
fc7ec1d9 356}
357
b5ecfcf0 358=head2 $self->prepare_body($c)
fc7ec1d9 359
4ab87e27 360sets up the L<Catalyst::Request> object body using L<HTTP::Body>
361
fc7ec1d9 362=cut
363
fbcc39ad 364sub prepare_body {
365 my ( $self, $c ) = @_;
99fe1710 366
398f13db 367 $c->request->prepare_body;
fc7ec1d9 368}
369
b5ecfcf0 370=head2 $self->prepare_body_chunk($c)
4bd82c41 371
4ab87e27 372Add a chunk to the request body.
373
4bd82c41 374=cut
375
398f13db 376# XXX - Can this be deleted?
4bd82c41 377sub prepare_body_chunk {
378 my ( $self, $c, $chunk ) = @_;
4f5ebacd 379
398f13db 380 $c->request->prepare_body_chunk($chunk);
4bd82c41 381}
382
b5ecfcf0 383=head2 $self->prepare_body_parameters($c)
06e1b616 384
b0ad47c1 385Sets up parameters from body.
4ab87e27 386
06e1b616 387=cut
388
fbcc39ad 389sub prepare_body_parameters {
390 my ( $self, $c ) = @_;
b0ad47c1 391
398f13db 392 $c->request->prepare_body_parameters;
44d28c7d 393}
fc7ec1d9 394
b5ecfcf0 395=head2 $self->prepare_parameters($c)
fc7ec1d9 396
4ab87e27 397sets up parameters from query and post parameters.
398
fc7ec1d9 399=cut
400
fbcc39ad 401sub prepare_parameters {
402 my ( $self, $c ) = @_;
fc7ec1d9 403
1cbdfa9b 404 $c->request->parameters;
fbcc39ad 405}
406
b5ecfcf0 407=head2 $self->prepare_path($c)
fc7ec1d9 408
4ab87e27 409abstract method, implemented by engines.
410
fc7ec1d9 411=cut
412
44d28c7d 413sub prepare_path {
414 my ($self, $ctx) = @_;
415
faa02805 416 my $env = $ctx->request->env;
44d28c7d 417
418 my $scheme = $ctx->request->secure ? 'https' : 'http';
419 my $host = $env->{HTTP_HOST} || $env->{SERVER_NAME};
420 my $port = $env->{SERVER_PORT} || 80;
421 my $base_path = $env->{SCRIPT_NAME} || "/";
422
423 # set the request URI
661de072 424 my $path;
425 if (!$ctx->config->{use_request_uri_for_path}) {
4904ee27 426 my $path_info = $env->{PATH_INFO};
427 if ( exists $env->{REDIRECT_URL} ) {
428 $base_path = $env->{REDIRECT_URL};
429 $base_path =~ s/\Q$path_info\E$//;
430 }
431 $path = $base_path . $path_info;
661de072 432 $path =~ s{^/+}{};
433 $path =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
434 $path =~ s/\?/%3F/g; # STUPID STUPID SPECIAL CASE
435 }
436 else {
437 my $req_uri = $env->{REQUEST_URI};
438 $req_uri =~ s/\?.*$//;
439 $path = $req_uri;
440 $path =~ s{^/+}{};
441 }
44d28c7d 442
443 # Using URI directly is way too slow, so we construct the URLs manually
444 my $uri_class = "URI::$scheme";
445
446 # HTTP_HOST will include the port even if it's 80/443
447 $host =~ s/:(?:80|443)$//;
448
449 if ($port !~ /^(?:80|443)$/ && $host !~ /:/) {
450 $host .= ":$port";
451 }
452
44d28c7d 453 my $query = $env->{QUERY_STRING} ? '?' . $env->{QUERY_STRING} : '';
454 my $uri = $scheme . '://' . $host . '/' . $path . $query;
455
4ee03d72 456 $ctx->request->uri( (bless \$uri, $uri_class)->canonical );
44d28c7d 457
458 # set the base URI
459 # base must end in a slash
460 $base_path .= '/' unless $base_path =~ m{/$};
461
462 my $base_uri = $scheme . '://' . $host . $base_path;
463
464 $ctx->request->base( bless \$base_uri, $uri_class );
465
466 return;
467}
fc7ec1d9 468
b5ecfcf0 469=head2 $self->prepare_request($c)
fc7ec1d9 470
b5ecfcf0 471=head2 $self->prepare_query_parameters($c)
fc7ec1d9 472
4ab87e27 473process the query string and extract query parameters.
474
fc7ec1d9 475=cut
476
e0616220 477sub prepare_query_parameters {
44d28c7d 478 my ($self, $c) = @_;
479
faa02805 480 my $env = $c->request->env;
481 my $query_string = exists $env->{QUERY_STRING}
482 ? $env->{QUERY_STRING}
44d28c7d 483 : '';
b0ad47c1 484
3b4d1251 485 # Check for keywords (no = signs)
486 # (yes, index() is faster than a regex :))
933ba403 487 if ( index( $query_string, '=' ) < 0 ) {
3b4d1251 488 $c->request->query_keywords( $self->unescape_uri($query_string) );
933ba403 489 return;
490 }
491
492 my %query;
e0616220 493
494 # replace semi-colons
495 $query_string =~ s/;/&/g;
b0ad47c1 496
2f381252 497 my @params = grep { length $_ } split /&/, $query_string;
e0616220 498
933ba403 499 for my $item ( @params ) {
b0ad47c1 500
501 my ($param, $value)
933ba403 502 = map { $self->unescape_uri($_) }
e5542b70 503 split( /=/, $item, 2 );
b0ad47c1 504
933ba403 505 $param = $self->unescape_uri($item) unless defined $param;
b0ad47c1 506
933ba403 507 if ( exists $query{$param} ) {
508 if ( ref $query{$param} ) {
509 push @{ $query{$param} }, $value;
510 }
511 else {
512 $query{$param} = [ $query{$param}, $value ];
513 }
514 }
515 else {
516 $query{$param} = $value;
517 }
e0616220 518 }
933ba403 519 $c->request->query_parameters( \%query );
e0616220 520}
fbcc39ad 521
b5ecfcf0 522=head2 $self->prepare_read($c)
fbcc39ad 523
47b9d68e 524Prepare to read by initializing the Content-Length from headers.
4ab87e27 525
fbcc39ad 526=cut
fc7ec1d9 527
fbcc39ad 528sub prepare_read {
529 my ( $self, $c ) = @_;
4f5ebacd 530
878b821c 531 # Initialize the amount of data we think we need to read
faa02805 532 $c->request->_read_length;
fbcc39ad 533}
fc7ec1d9 534
b5ecfcf0 535=head2 $self->prepare_request(@arguments)
fc7ec1d9 536
c4a17516 537Populate the context object from the request object.
4ab87e27 538
fc7ec1d9 539=cut
540
44d28c7d 541sub prepare_request {
542 my ($self, $ctx, %args) = @_;
faa02805 543 $ctx->request->_set_env($args{env});
544 $self->_set_env($args{env}); # Nasty back compat!
545 $ctx->response->_set_response_cb($args{response_cb});
44d28c7d 546}
fc7ec1d9 547
b5ecfcf0 548=head2 $self->prepare_uploads($c)
c9afa5fc 549
fbcc39ad 550=cut
551
552sub prepare_uploads {
553 my ( $self, $c ) = @_;
7fa2c9c1 554
555 my $request = $c->request;
0f56bbcf 556 return unless $request->_body;
7fa2c9c1 557
0f56bbcf 558 my $uploads = $request->_body->upload;
7fa2c9c1 559 my $parameters = $request->parameters;
91772de9 560 foreach my $name (keys %$uploads) {
561 my $files = $uploads->{$name};
fbcc39ad 562 my @uploads;
7fa2c9c1 563 for my $upload (ref $files eq 'ARRAY' ? @$files : ($files)) {
564 my $headers = HTTP::Headers->new( %{ $upload->{headers} } );
565 my $u = Catalyst::Request::Upload->new
566 (
567 size => $upload->{size},
a160c98d 568 type => scalar $headers->content_type,
7fa2c9c1 569 headers => $headers,
570 tempname => $upload->{tempname},
571 filename => $upload->{filename},
572 );
fbcc39ad 573 push @uploads, $u;
574 }
7fa2c9c1 575 $request->uploads->{$name} = @uploads > 1 ? \@uploads : $uploads[0];
f4a57de4 576
c4bed79a 577 # support access to the filename as a normal param
578 my @filenames = map { $_->{filename} } @uploads;
a7e05d9d 579 # append, if there's already params with this name
7fa2c9c1 580 if (exists $parameters->{$name}) {
581 if (ref $parameters->{$name} eq 'ARRAY') {
582 push @{ $parameters->{$name} }, @filenames;
a7e05d9d 583 }
584 else {
7fa2c9c1 585 $parameters->{$name} = [ $parameters->{$name}, @filenames ];
a7e05d9d 586 }
587 }
588 else {
7fa2c9c1 589 $parameters->{$name} = @filenames > 1 ? \@filenames : $filenames[0];
a7e05d9d 590 }
fbcc39ad 591 }
592}
593
767480fd 594=head2 $self->write($c, $buffer)
c9afa5fc 595
767480fd 596Writes the buffer to the client.
4ab87e27 597
c9afa5fc 598=cut
599
767480fd 600sub write {
601 my ( $self, $c, $buffer ) = @_;
602
603 $c->response->write($buffer);
604}
fbcc39ad 605
b5ecfcf0 606=head2 $self->read($c, [$maxlength])
fbcc39ad 607
ea72fece 608Reads from the input stream by calling C<< $self->read_chunk >>.
609
610Maintains the read_length and read_position counters as data is read.
611
fbcc39ad 612=cut
613
614sub read {
615 my ( $self, $c, $maxlength ) = @_;
4f5ebacd 616
f083854e 617 $c->request->read($maxlength);
fbcc39ad 618}
fc7ec1d9 619
87f50436 620=head2 $self->read_chunk($c, \$buffer, $length)
23f9d934 621
10011c19 622Each engine implements read_chunk as its preferred way of reading a chunk
ea72fece 623of data. Returns the number of bytes read. A return of 0 indicates that
624there is no more data to be read.
fc7ec1d9 625
fbcc39ad 626=cut
61b1e958 627
e6b46d80 628sub read_chunk {
ce7abbda 629 my ($self, $ctx) = (shift, shift);
87f50436 630 return $ctx->request->read_chunk(@_);
e6b46d80 631}
61b1e958 632
9560b708 633=head2 $self->run($app, $server)
63b763c5 634
9560b708 635Start the engine. Builds a PSGI application and calls the
acbecf08 636run method on the server passed in, which then causes the
637engine to loop, handling requests..
4ab87e27 638
fbcc39ad 639=cut
fc7ec1d9 640
44d28c7d 641sub run {
51857616 642 my ($self, $app, $psgi, @args) = @_;
acbecf08 643 # @args left here rather than just a $options, $server for back compat with the
644 # old style scripts which send a few args, then a hashref
645
646 # They should never actually be used in the normal case as the Plack engine is
647 # passed in got all the 'standard' args via the loader in the script already.
648
649 # FIXME - we should stash the options in an attribute so that custom args
650 # like Gitalist's --git_dir are possible to get from the app without stupid tricks.
1e5dad00 651 my $server = pop @args if (scalar @args && blessed $args[-1]);
652 my $options = pop @args if (scalar @args && ref($args[-1]) eq 'HASH');
ccb13b15 653 # Back compat hack for applications with old (non Catalyst::Script) scripts to work in FCGI.
654 if (scalar @args && !ref($args[0])) {
655 if (my $listen = shift @args) {
656 $options->{listen} ||= [$listen];
657 }
658 }
acbecf08 659 if (! $server ) {
f7a3f8fd 660 $server = Catalyst::EngineLoader->new(application_name => ref($self))->auto(%$options);
f7f55b2f 661 # We're not being called from a script, so auto detect what backend to
662 # run on. This should never happen, as mod_perl never calls ->run,
663 # instead the $app->handle method is called per request.
acbecf08 664 $app->log->warn("Not supplied a Plack engine, falling back to engine auto-loader (are your scripts ancient?)")
665 }
aee7cdcc 666 $app->run_options($options);
acbecf08 667 $server->run($psgi, $options);
a1791811 668}
44d28c7d 669
9560b708 670=head2 build_psgi_app ($app, @args)
671
e3f6b891 672Builds and returns a PSGI application closure. (Raw, not wrapped in middleware)
9560b708 673
674=cut
675
22a5833d 676sub build_psgi_app {
a1791811 677 my ($self, $app, @args) = @_;
c2f4a965 678
fcffcb05 679 return sub {
44d28c7d 680 my ($env) = @_;
681
682 return sub {
683 my ($respond) = @_;
f2b83422 684 confess("Did not get a response callback for writer, cannot continiue") unless $respond;
faa02805 685 $app->handle_request(env => $env, response_cb => $respond);
44d28c7d 686 };
687 };
688}
fc7ec1d9 689
933ba403 690=head2 $self->unescape_uri($uri)
691
6a44fe01 692Unescapes a given URI using the most efficient method available. Engines such
693as Apache may implement this using Apache's C-based modules, for example.
933ba403 694
695=cut
696
697sub unescape_uri {
8c7d83e1 698 my ( $self, $str ) = @_;
7d22a537 699
700 $str =~ s/(?:%([0-9A-Fa-f]{2})|\+)/defined $1 ? chr(hex($1)) : ' '/eg;
701
8c7d83e1 702 return $str;
933ba403 703}
34d28dfd 704
4ab87e27 705=head2 $self->finalize_output
706
707<obsolete>, see finalize_body
708
0c76ec45 709=head2 $self->env
710
6356febf 711Hash containing environment variables including many special variables inserted
0c76ec45 712by WWW server - like SERVER_*, REMOTE_*, HTTP_* ...
713
6356febf 714Before accessing environment variables consider whether the same information is
0c76ec45 715not directly available via Catalyst objects $c->request, $c->engine ...
716
6356febf 717BEWARE: If you really need to access some environment variable from your Catalyst
0c76ec45 718application you should use $c->engine->env->{VARNAME} instead of $ENV{VARNAME},
ae7da8f5 719as in some environments the %ENV hash does not contain what you would expect.
0c76ec45 720
fbcc39ad 721=head1 AUTHORS
722
2f381252 723Catalyst Contributors, see Catalyst.pm
fc7ec1d9 724
725=head1 COPYRIGHT
726
536bee89 727This library is free software. You can redistribute it and/or modify it under
fc7ec1d9 728the same terms as Perl itself.
729
730=cut
731
7321;