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