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