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