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