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