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