update distar url
[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;
d495753a 16use namespace::clean -except => 'meta';
da57fe99 17use utf8;
d495753a 18
faa02805 19# Amount of data to read from input on each pass
20our $CHUNKSIZE = 64 * 1024;
a50e5b46 21
52af5159 22# XXX - this is only here for compat, do not use!
ee2c12fd 23has env => ( is => 'rw', writer => '_set_env' , weak_ref=>1);
52af5159 24my $WARN_ABOUT_ENV = 0;
25around env => sub {
26 my ($orig, $self, @args) = @_;
27 if(@args) {
28 warn "env as a writer is deprecated, you probably need to upgrade Catalyst::Engine::PSGI"
29 unless $WARN_ABOUT_ENV++;
30 return $self->_set_env(@args);
31 }
32 return $self->$orig;
33};
34
35# XXX - Only here for Engine::PSGI compat
36sub prepare_connection {
37 my ($self, $ctx) = @_;
38 $ctx->request->prepare_connection;
39}
40
fc7ec1d9 41=head1 NAME
42
43Catalyst::Engine - The Catalyst Engine
44
45=head1 SYNOPSIS
46
47See L<Catalyst>.
48
49=head1 DESCRIPTION
50
23f9d934 51=head1 METHODS
fc7ec1d9 52
cd3bb248 53
b5ecfcf0 54=head2 $self->finalize_body($c)
06e1b616 55
717fc5c9 56Finalize body. Prints the response output as blocking stream if it looks like
57a filehandle, otherwise write it out all in one go. If there is no body in
58the response, we assume you are handling it 'manually', such as for nonblocking
fc89737e 59style or asynchronous streaming responses. You do this by calling L</write>
60several times (which sends HTTP headers if needed) or you close over
61C<< $response->write_fh >>.
e37f92f5 62
fc89737e 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 ) = @_;
46fff667 69 my $res = $c->response; # We use this all over
70
71 ## If we've asked for the write 'filehandle' that means the application is
72 ## doing something custom and is expected to close the response
73 return if $res->_has_write_fh;
74
8a3dcb98 75 my $body = $res->body; # save some typing
46fff667 76 if($res->_has_response_cb) {
77 ## we have not called the response callback yet, so we are safe to send
78 ## the whole body to PSGI
88e5a8b0 79
46fff667 80 my @headers;
81 $res->headers->scan(sub { push @headers, @_ });
82
8a3dcb98 83 # We need to figure out what kind of body we have and normalize it to something
84 # PSGI can deal with
46fff667 85 if(defined $body) {
8a3dcb98 86 # Handle objects first
87 if(blessed($body)) {
88 if($body->can('getline')) {
89 # Body is an IO handle that meets the PSGI spec. Nothing to normalize
90 } elsif($body->can('read')) {
91
92 # In the past, Catalyst only looked for ->read not ->getline. It is very possible
93 # that one might have an object that respected read but did not have getline.
94 # As a result, we need to handle this case for backcompat.
88e5a8b0 95
8a3dcb98 96 # We will just do the old loop for now. In a future version of Catalyst this support
88e5a8b0 97 # will be removed and one will have to rewrite their custom object or use
8a3dcb98 98 # Plack::Middleware::AdaptFilehandleRead. In anycase support for this is officially
99 # deprecated and described as such as of 5.90060
88e5a8b0 100
8a3dcb98 101 my $got;
102 do {
103 $got = read $body, my ($buffer), $CHUNKSIZE;
104 $got = 0 unless $self->write($c, $buffer );
105 } while $got > 0;
106
107 close $body;
108 return;
109 } else {
110 # Looks like for backcompat reasons we need to be able to deal
111 # with stringyfiable objects.
88e5a8b0 112 $body = ["$body"];
8a3dcb98 113 }
114 } elsif(ref $body) {
115 if( (ref($body) eq 'GLOB') or (ref($body) eq 'ARRAY')) {
116 # Again, PSGI can just accept this, no transform needed. We don't officially
117 # document the body as arrayref at this time (and there's not specific test
118 # cases. we support it because it simplifies some plack compatibility logic
119 # and we might make it official at some point.
120 } else {
121 $c->log->error("${\ref($body)} is not a valid value for Response->body");
122 return;
123 }
46fff667 124 } else {
8a3dcb98 125 # Body is defined and not an object or reference. We assume a simple value
126 # and wrap it in an array for PSGI
127 $body = [$body];
46fff667 128 }
129 } else {
8a3dcb98 130 # There's no body...
131 $body = [];
46fff667 132 }
46fff667 133 $res->_response_cb->([ $res->status, \@headers, $body]);
134 $res->_clear_response_cb;
135
136 } else {
137 ## Now, if there's no response callback anymore, that means someone has
138 ## called ->write in order to stream 'some stuff along the way'. I think
139 ## for backcompat we still need to handle a ->body. I guess I could see
140 ## someone calling ->write to presend some stuff, and then doing the rest
141 ## via ->body, like in a template.
88e5a8b0 142
46fff667 143 ## We'll just use the old, existing code for this (or most of it)
144
145 if(my $body = $res->body) {
8a3dcb98 146
46fff667 147 if ( blessed($body) && $body->can('read') or ref($body) eq 'GLOB' ) {
148
149 ## In this case we have no choice and will fall back on the old
8a3dcb98 150 ## manual streaming stuff. Not optimal. This is deprecated as of 5.900560+
46fff667 151
152 my $got;
153 do {
154 $got = read $body, my ($buffer), $CHUNKSIZE;
155 $got = 0 unless $self->write($c, $buffer );
156 } while $got > 0;
157
158 close $body;
159 }
160 else {
88e5a8b0 161
9c056c82 162 # Case where body was set after calling ->write. We'd prefer not to
e27f6633 163 # support this, but I can see some use cases with the way most of the
9c056c82 164 # views work. Since body has already been encoded, we need to do
165 # an 'unencoded_write' here.
166 $self->unencoded_write( $c, $body );
46fff667 167 }
168 }
169
170 $res->_writer->close;
171 $res->_clear_writer;
f4a57de4 172 }
030674d0 173
ca3023fc 174 return;
fbcc39ad 175}
6dc87a0f 176
b5ecfcf0 177=head2 $self->finalize_cookies($c)
6dc87a0f 178
fa32ac82 179Create CGI::Simple::Cookie objects from $c->res->cookies, and set them as
180response headers.
4ab87e27 181
6dc87a0f 182=cut
183
184sub finalize_cookies {
fbcc39ad 185 my ( $self, $c ) = @_;
6dc87a0f 186
fbcc39ad 187 my @cookies;
7fa2c9c1 188 my $response = $c->response;
c82ed742 189
91772de9 190 foreach my $name (keys %{ $response->cookies }) {
191
192 my $val = $response->cookies->{$name};
fbcc39ad 193
2832cb5d 194 my $cookie = (
7e95ba12 195 blessed($val)
2832cb5d 196 ? $val
197 : CGI::Simple::Cookie->new(
198 -name => $name,
199 -value => $val->{value},
200 -expires => $val->{expires},
201 -domain => $val->{domain},
202 -path => $val->{path},
b21bc468 203 -secure => $val->{secure} || 0,
204 -httponly => $val->{httponly} || 0,
9d5a2735 205 -samesite => $val->{samesite},
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: "
1565e158 237 . $val->{'__MOP__'} if ref $val eq 'HASH' && 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));
88e5a8b0 253
361ba9b2 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 }
447 (ref $upload eq 'ARRAY' ? @{$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
2a2b8f65 596 $query_string =~ s/\A[&;]+//;
e0616220 597
97f38a92 598 my @unsplit_pairs = split /[&;]+/, $query_string;
599 my $p = Hash::MultiValue->new();
600
601 my $is_first_pair = 1;
602 for my $pair (@unsplit_pairs) {
603 my ($name, $value)
604 = map { defined $_ ? $decoder->($self->unescape_uri($_)) : $_ }
324b08a4 605 ( split /=/, $pair, 2 )[0,1]; # slice forces two elements
97f38a92 606
607 if ($is_first_pair) {
608 # If the first pair has no equal sign, then it means the isindex
609 # flag is set.
610 $c->request->query_keywords($name) unless defined $value;
611
612 $is_first_pair = 0;
613 }
614
615 $p->add( $name => $value );
616 }
617
b0ad47c1 618
103f2d96 619 $c->encoding($old_encoding) if $old_encoding;
2a2b8f65 620 $c->request->query_parameters( $c->request->_use_hash_multivalue ? $p : $p->mixed );
e0616220 621}
fbcc39ad 622
b5ecfcf0 623=head2 $self->prepare_read($c)
fbcc39ad 624
47b9d68e 625Prepare to read by initializing the Content-Length from headers.
4ab87e27 626
fbcc39ad 627=cut
fc7ec1d9 628
fbcc39ad 629sub prepare_read {
630 my ( $self, $c ) = @_;
4f5ebacd 631
878b821c 632 # Initialize the amount of data we think we need to read
faa02805 633 $c->request->_read_length;
fbcc39ad 634}
fc7ec1d9 635
b5ecfcf0 636=head2 $self->prepare_request(@arguments)
fc7ec1d9 637
c4a17516 638Populate the context object from the request object.
4ab87e27 639
fc7ec1d9 640=cut
641
44d28c7d 642sub prepare_request {
643 my ($self, $ctx, %args) = @_;
0eb98ebd 644 $ctx->log->psgienv($args{env}) if $ctx->log->can('psgienv');
faa02805 645 $ctx->request->_set_env($args{env});
52af5159 646 $self->_set_env($args{env}); # Nasty back compat!
faa02805 647 $ctx->response->_set_response_cb($args{response_cb});
44d28c7d 648}
fc7ec1d9 649
b5ecfcf0 650=head2 $self->prepare_uploads($c)
c9afa5fc 651
fbcc39ad 652=cut
653
654sub prepare_uploads {
655 my ( $self, $c ) = @_;
7fa2c9c1 656
657 my $request = $c->request;
0f56bbcf 658 return unless $request->_body;
7fa2c9c1 659
4a62800d 660 my $enc = $c->encoding;
0f56bbcf 661 my $uploads = $request->_body->upload;
7fa2c9c1 662 my $parameters = $request->parameters;
91772de9 663 foreach my $name (keys %$uploads) {
664 my $files = $uploads->{$name};
0d6aa161 665 $name = $c->_handle_unicode_decoding($name) if $enc;
fbcc39ad 666 my @uploads;
7fa2c9c1 667 for my $upload (ref $files eq 'ARRAY' ? @$files : ($files)) {
668 my $headers = HTTP::Headers->new( %{ $upload->{headers} } );
4a62800d 669 my $filename = $upload->{filename};
670 $filename = $c->_handle_unicode_decoding($filename) if $enc;
671
7fa2c9c1 672 my $u = Catalyst::Request::Upload->new
673 (
674 size => $upload->{size},
a160c98d 675 type => scalar $headers->content_type,
6adc45cf 676 charset => scalar $headers->content_type_charset,
7fa2c9c1 677 headers => $headers,
678 tempname => $upload->{tempname},
4a62800d 679 filename => $filename,
7fa2c9c1 680 );
fbcc39ad 681 push @uploads, $u;
682 }
7fa2c9c1 683 $request->uploads->{$name} = @uploads > 1 ? \@uploads : $uploads[0];
f4a57de4 684
c4bed79a 685 # support access to the filename as a normal param
686 my @filenames = map { $_->{filename} } @uploads;
a7e05d9d 687 # append, if there's already params with this name
7fa2c9c1 688 if (exists $parameters->{$name}) {
689 if (ref $parameters->{$name} eq 'ARRAY') {
690 push @{ $parameters->{$name} }, @filenames;
a7e05d9d 691 }
692 else {
7fa2c9c1 693 $parameters->{$name} = [ $parameters->{$name}, @filenames ];
a7e05d9d 694 }
695 }
696 else {
7fa2c9c1 697 $parameters->{$name} = @filenames > 1 ? \@filenames : $filenames[0];
a7e05d9d 698 }
fbcc39ad 699 }
700}
701
767480fd 702=head2 $self->write($c, $buffer)
c9afa5fc 703
767480fd 704Writes the buffer to the client.
4ab87e27 705
c9afa5fc 706=cut
707
767480fd 708sub write {
709 my ( $self, $c, $buffer ) = @_;
710
711 $c->response->write($buffer);
712}
fbcc39ad 713
9c056c82 714=head2 $self->unencoded_write($c, $buffer)
715
716Writes the buffer to the client without encoding. Necessary for
717already encoded buffers. Used when a $c->write has been done
718followed by $c->res->body.
719
720=cut
721
722sub unencoded_write {
723 my ( $self, $c, $buffer ) = @_;
724
725 $c->response->unencoded_write($buffer);
726}
727
b5ecfcf0 728=head2 $self->read($c, [$maxlength])
fbcc39ad 729
ea72fece 730Reads from the input stream by calling C<< $self->read_chunk >>.
731
732Maintains the read_length and read_position counters as data is read.
733
fbcc39ad 734=cut
735
736sub read {
737 my ( $self, $c, $maxlength ) = @_;
4f5ebacd 738
f083854e 739 $c->request->read($maxlength);
fbcc39ad 740}
fc7ec1d9 741
87f50436 742=head2 $self->read_chunk($c, \$buffer, $length)
23f9d934 743
10011c19 744Each engine implements read_chunk as its preferred way of reading a chunk
ea72fece 745of data. Returns the number of bytes read. A return of 0 indicates that
746there is no more data to be read.
fc7ec1d9 747
fbcc39ad 748=cut
61b1e958 749
e6b46d80 750sub read_chunk {
ce7abbda 751 my ($self, $ctx) = (shift, shift);
87f50436 752 return $ctx->request->read_chunk(@_);
e6b46d80 753}
61b1e958 754
9560b708 755=head2 $self->run($app, $server)
63b763c5 756
9560b708 757Start the engine. Builds a PSGI application and calls the
acbecf08 758run method on the server passed in, which then causes the
759engine to loop, handling requests..
4ab87e27 760
fbcc39ad 761=cut
fc7ec1d9 762
44d28c7d 763sub run {
51857616 764 my ($self, $app, $psgi, @args) = @_;
acbecf08 765 # @args left here rather than just a $options, $server for back compat with the
766 # old style scripts which send a few args, then a hashref
767
768 # They should never actually be used in the normal case as the Plack engine is
769 # passed in got all the 'standard' args via the loader in the script already.
770
771 # FIXME - we should stash the options in an attribute so that custom args
772 # like Gitalist's --git_dir are possible to get from the app without stupid tricks.
1e5dad00 773 my $server = pop @args if (scalar @args && blessed $args[-1]);
774 my $options = pop @args if (scalar @args && ref($args[-1]) eq 'HASH');
ccb13b15 775 # Back compat hack for applications with old (non Catalyst::Script) scripts to work in FCGI.
776 if (scalar @args && !ref($args[0])) {
777 if (my $listen = shift @args) {
778 $options->{listen} ||= [$listen];
779 }
780 }
acbecf08 781 if (! $server ) {
f7a3f8fd 782 $server = Catalyst::EngineLoader->new(application_name => ref($self))->auto(%$options);
f7f55b2f 783 # We're not being called from a script, so auto detect what backend to
784 # run on. This should never happen, as mod_perl never calls ->run,
785 # instead the $app->handle method is called per request.
acbecf08 786 $app->log->warn("Not supplied a Plack engine, falling back to engine auto-loader (are your scripts ancient?)")
787 }
aee7cdcc 788 $app->run_options($options);
acbecf08 789 $server->run($psgi, $options);
a1791811 790}
44d28c7d 791
9560b708 792=head2 build_psgi_app ($app, @args)
793
e3f6b891 794Builds and returns a PSGI application closure. (Raw, not wrapped in middleware)
9560b708 795
796=cut
797
22a5833d 798sub build_psgi_app {
a1791811 799 my ($self, $app, @args) = @_;
c2f4a965 800
fcffcb05 801 return sub {
44d28c7d 802 my ($env) = @_;
803
804 return sub {
805 my ($respond) = @_;
e33d788d 806 confess("Did not get a response callback for writer, cannot continue") unless $respond;
faa02805 807 $app->handle_request(env => $env, response_cb => $respond);
44d28c7d 808 };
809 };
810}
fc7ec1d9 811
933ba403 812=head2 $self->unescape_uri($uri)
813
6a44fe01 814Unescapes a given URI using the most efficient method available. Engines such
815as Apache may implement this using Apache's C-based modules, for example.
933ba403 816
817=cut
818
819sub unescape_uri {
8c7d83e1 820 my ( $self, $str ) = @_;
7d22a537 821
822 $str =~ s/(?:%([0-9A-Fa-f]{2})|\+)/defined $1 ? chr(hex($1)) : ' '/eg;
823
8c7d83e1 824 return $str;
933ba403 825}
34d28dfd 826
4ab87e27 827=head2 $self->finalize_output
828
829<obsolete>, see finalize_body
830
0c76ec45 831=head2 $self->env
832
6356febf 833Hash containing environment variables including many special variables inserted
0c76ec45 834by WWW server - like SERVER_*, REMOTE_*, HTTP_* ...
835
6356febf 836Before accessing environment variables consider whether the same information is
0c76ec45 837not directly available via Catalyst objects $c->request, $c->engine ...
838
6356febf 839BEWARE: If you really need to access some environment variable from your Catalyst
0c76ec45 840application you should use $c->engine->env->{VARNAME} instead of $ENV{VARNAME},
ae7da8f5 841as in some environments the %ENV hash does not contain what you would expect.
0c76ec45 842
fbcc39ad 843=head1 AUTHORS
844
2f381252 845Catalyst Contributors, see Catalyst.pm
fc7ec1d9 846
847=head1 COPYRIGHT
848
536bee89 849This library is free software. You can redistribute it and/or modify it under
fc7ec1d9 850the same terms as Perl itself.
851
852=cut
853
58f86b1a 854__PACKAGE__->meta->make_immutable;
855
fc7ec1d9 8561;