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