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