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