Merged 5.49_01 (r1339) from refactored branch to trunk
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Engine.pm
CommitLineData
fc7ec1d9 1package Catalyst::Engine;
2
3use strict;
fbcc39ad 4use base 'Class::Accessor::Fast';
6dc87a0f 5use CGI::Cookie;
fc7ec1d9 6use Data::Dumper;
7use HTML::Entities;
fbcc39ad 8use HTTP::Body;
fc7ec1d9 9use HTTP::Headers;
fbcc39ad 10
11# input position and length
12__PACKAGE__->mk_accessors( qw/read_position read_length/ );
13
14# Stringify to class
15use overload '""' => sub { return ref shift }, fallback => 1;
fc7ec1d9 16
17=head1 NAME
18
19Catalyst::Engine - The Catalyst Engine
20
21=head1 SYNOPSIS
22
23See L<Catalyst>.
24
25=head1 DESCRIPTION
26
23f9d934 27=head1 METHODS
fc7ec1d9 28
23f9d934 29=over 4
30
fbcc39ad 31=item $self->finalize_output
cd3bb248 32
33<obsolete>, see finalize_body
34
fbcc39ad 35=item $self->finalize_body($c)
06e1b616 36
fbcc39ad 37Finalize body. Prints the response output.
06e1b616 38
39=cut
40
fbcc39ad 41sub finalize_body {
42 my ( $self, $c ) = @_;
43
44 $self->write( $c, $c->response->output );
45}
6dc87a0f 46
fbcc39ad 47=item $self->finalize_cookies($c)
6dc87a0f 48
49=cut
50
51sub finalize_cookies {
fbcc39ad 52 my ( $self, $c ) = @_;
6dc87a0f 53
fbcc39ad 54 my @cookies;
6dc87a0f 55 while ( my ( $name, $cookie ) = each %{ $c->response->cookies } ) {
fbcc39ad 56
6dc87a0f 57 my $cookie = CGI::Cookie->new(
58 -name => $name,
59 -value => $cookie->{value},
60 -expires => $cookie->{expires},
61 -domain => $cookie->{domain},
62 -path => $cookie->{path},
63 -secure => $cookie->{secure} || 0
64 );
65
fbcc39ad 66 push @cookies, $cookie->as_string;
6dc87a0f 67 }
6dc87a0f 68
fbcc39ad 69 if (@cookies) {
70 $c->res->headers->push_header( 'Set-Cookie' => join ',', @cookies );
71 }
72}
969647fd 73
fbcc39ad 74=item $self->finalize_error($c)
969647fd 75
76=cut
77
78sub finalize_error {
fbcc39ad 79 my ( $self, $c ) = @_;
969647fd 80
81 $c->res->headers->content_type('text/html');
82 my $name = $c->config->{name} || 'Catalyst Application';
83
84 my ( $title, $error, $infos );
85 if ( $c->debug ) {
62d9b030 86
87 # For pretty dumps
88 local $Data::Dumper::Terse = 1;
89 $error = join '',
90 map { '<code class="error">' . encode_entities($_) . '</code>' }
91 @{ $c->error };
969647fd 92 $error ||= 'No output';
93 $title = $name = "$name on Catalyst $Catalyst::VERSION";
fbcc39ad 94
95 # Don't show context in the dump
96 delete $c->req->{_context};
97 delete $c->res->{_context};
98
99 # Don't show body parser in the dump
100 delete $c->req->{_body};
101
102 # Don't show response header state in dump
103 delete $c->res->{_finalized_headers};
104
969647fd 105 my $req = encode_entities Dumper $c->req;
106 my $res = encode_entities Dumper $c->res;
107 my $stash = encode_entities Dumper $c->stash;
108 $infos = <<"";
109<br/>
110<b><u>Request</u></b><br/>
111<pre>$req</pre>
112<b><u>Response</u></b><br/>
113<pre>$res</pre>
114<b><u>Stash</u></b><br/>
115<pre>$stash</pre>
116
117 }
118 else {
119 $title = $name;
120 $error = '';
121 $infos = <<"";
122<pre>
123(en) Please come back later
124(de) Bitte versuchen sie es spaeter nocheinmal
125(nl) Gelieve te komen later terug
126(no) Vennligst prov igjen senere
127(fr) Veuillez revenir plus tard
128(es) Vuelto por favor mas adelante
129(pt) Voltado por favor mais tarde
fbcc39ad 130(it) Ritornato prego piĆ¹ successivamente
969647fd 131</pre>
132
133 $name = '';
134 }
e060fe05 135 $c->res->body( <<"" );
969647fd 136<html>
137<head>
138 <title>$title</title>
139 <style type="text/css">
140 body {
141 font-family: "Bitstream Vera Sans", "Trebuchet MS", Verdana,
142 Tahoma, Arial, helvetica, sans-serif;
143 color: #ddd;
144 background-color: #eee;
145 margin: 0px;
146 padding: 0px;
147 }
148 div.box {
149 background-color: #ccc;
150 border: 1px solid #aaa;
151 padding: 4px;
152 margin: 10px;
153 -moz-border-radius: 10px;
154 }
155 div.error {
156 background-color: #977;
157 border: 1px solid #755;
158 padding: 8px;
159 margin: 4px;
160 margin-bottom: 10px;
161 -moz-border-radius: 10px;
162 }
163 div.infos {
164 background-color: #797;
165 border: 1px solid #575;
166 padding: 8px;
167 margin: 4px;
168 margin-bottom: 10px;
169 -moz-border-radius: 10px;
170 }
171 div.name {
172 background-color: #779;
173 border: 1px solid #557;
174 padding: 8px;
175 margin: 4px;
176 -moz-border-radius: 10px;
177 }
7f8e0078 178 code.error {
179 display: block;
180 margin: 1em 0;
181 overflow: auto;
182 white-space: pre;
183 }
969647fd 184 </style>
185</head>
186<body>
187 <div class="box">
188 <div class="error">$error</div>
189 <div class="infos">$infos</div>
190 <div class="name">$name</div>
191 </div>
192</body>
193</html>
194
195}
196
fbcc39ad 197=item $self->finalize_headers($c)
fc7ec1d9 198
199=cut
200
201sub finalize_headers { }
202
fbcc39ad 203=item $self->finalize_read($c)
fc7ec1d9 204
205=cut
206
fbcc39ad 207sub finalize_read {
208 my ( $self, $c ) = @_;
209
210 undef $self->{_prepared_read};
fc7ec1d9 211}
212
fbcc39ad 213=item $self->finalize_uploads($c)
fc7ec1d9 214
215=cut
216
fbcc39ad 217sub finalize_uploads {
218 my ( $self, $c ) = @_;
99fe1710 219
fbcc39ad 220 if ( keys %{ $c->request->uploads } ) {
221 for my $key ( keys %{ $c->request->uploads } ) {
222 my $upload = $c->request->uploads->{$key};
223 unlink map { $_->tempname }
224 grep { -e $_->tempname }
225 ref $upload eq 'ARRAY' ? @{$upload} : ($upload);
c85ff642 226 }
c85ff642 227 }
fc7ec1d9 228}
229
fbcc39ad 230=item $self->prepare_body($c)
fc7ec1d9 231
232=cut
233
fbcc39ad 234sub prepare_body {
235 my ( $self, $c ) = @_;
99fe1710 236
fbcc39ad 237 $self->read_length( $c->request->header('Content-Length') || 0 );
238 my $type = $c->request->header('Content-Type');
99fe1710 239
fbcc39ad 240 unless ( $c->request->{_body} ) {
241 $c->request->{_body} = HTTP::Body->new( $type, $self->read_length );
fc7ec1d9 242 }
fbcc39ad 243
244 if ( $self->read_length > 0 ) {
245 while ( my $buffer = $self->read( $c ) ) {
246 $c->request->{_body}->add( $buffer );
247 }
fc7ec1d9 248 }
249}
250
fbcc39ad 251=item $self->prepare_body_parameters($c)
06e1b616 252
253=cut
254
fbcc39ad 255sub prepare_body_parameters {
256 my ( $self, $c ) = @_;
257 $c->request->body_parameters( $c->request->{_body}->param );
258}
0556eb49 259
fbcc39ad 260=item $self->prepare_connection($c)
0556eb49 261
262=cut
263
264sub prepare_connection { }
265
fbcc39ad 266=item $self->prepare_cookies($c)
fc7ec1d9 267
268=cut
269
6dc87a0f 270sub prepare_cookies {
fbcc39ad 271 my ( $self, $c ) = @_;
6dc87a0f 272
273 if ( my $header = $c->request->header('Cookie') ) {
274 $c->req->cookies( { CGI::Cookie->parse($header) } );
275 }
276}
fc7ec1d9 277
fbcc39ad 278=item $self->prepare_headers($c)
fc7ec1d9 279
280=cut
281
282sub prepare_headers { }
283
fbcc39ad 284=item $self->prepare_parameters($c)
fc7ec1d9 285
286=cut
287
fbcc39ad 288sub prepare_parameters {
289 my ( $self, $c ) = @_;
fc7ec1d9 290
fbcc39ad 291 # We copy, no references
292 while ( my ( $name, $param ) = each %{ $c->request->query_parameters } ) {
293 $param = ref $param eq 'ARRAY' ? [ @{$param} ] : $param;
294 $c->request->parameters->{$name} = $param;
295 }
fc7ec1d9 296
fbcc39ad 297 # Merge query and body parameters
298 while ( my ( $name, $param ) = each %{ $c->request->body_parameters } ) {
299 $param = ref $param eq 'ARRAY' ? [ @{$param} ] : $param;
300 if ( my $old_param = $c->request->parameters->{$name} ) {
301 if ( ref $old_param eq 'ARRAY' ) {
302 push @{ $c->request->parameters->{$name} },
303 ref $param eq 'ARRAY' ? @$param : $param;
304 }
305 else { $c->request->parameters->{$name} = [ $old_param, $param ] }
306 }
307 else { $c->request->parameters->{$name} = $param }
308 }
309}
310
311=item $self->prepare_path($c)
fc7ec1d9 312
313=cut
314
315sub prepare_path { }
316
fbcc39ad 317=item $self->prepare_request($c)
fc7ec1d9 318
fbcc39ad 319=item $self->prepare_query_parameters($c)
fc7ec1d9 320
321=cut
322
fbcc39ad 323sub prepare_query_parameters { }
324
325=item $self->prepare_read($c)
326
327=cut
fc7ec1d9 328
fbcc39ad 329sub prepare_read {
330 my ( $self, $c ) = @_;
331
332 # Reset the read position
333 $self->read_position( 0 );
334}
fc7ec1d9 335
fbcc39ad 336=item $self->prepare_request(@arguments)
fc7ec1d9 337
338=cut
339
fbcc39ad 340sub prepare_request { }
fc7ec1d9 341
fbcc39ad 342=item $self->prepare_uploads($c)
c9afa5fc 343
fbcc39ad 344=cut
345
346sub prepare_uploads {
347 my ( $self, $c ) = @_;
348 my $uploads = $c->request->{_body}->upload;
349 for my $name ( keys %$uploads ) {
350 my $files = $uploads->{$name};
351 $files = ref $files eq 'ARRAY' ? $files : [$files];
352 my @uploads;
353 for my $upload (@$files) {
354 my $u = Catalyst::Request::Upload->new;
355 $u->headers( HTTP::Headers->new( %{ $upload->{headers} } ) );
356 $u->type( $u->headers->content_type );
357 $u->tempname( $upload->{tempname} );
358 $u->size( $upload->{size} );
359 $u->filename( $upload->{filename} );
360 push @uploads, $u;
361 }
362 $c->request->uploads->{$name} = @uploads > 1 ? \@uploads : $uploads[0];
363 }
364}
365
366=item $self->prepare_write($c)
c9afa5fc 367
368=cut
369
fbcc39ad 370sub prepare_write { }
371
372=item $self->read($c, [$maxlength])
373
374=cut
375
376sub read {
377 my ( $self, $c, $maxlength ) = @_;
378
379 unless ( $self->{_prepared_read} ) {
380 $self->prepare_read( $c );
381 $self->{_prepared_read} = 1;
382 }
383
384 my $remaining = $self->read_length - $self->read_position;
385 $maxlength ||= $self->read_length;
386
387 # Are we done reading?
388 if ( $remaining <= 0 ) {
389 $self->finalize_read( $c );
390 return;
391 }
c9afa5fc 392
fbcc39ad 393 my $readlen = ( $remaining > $maxlength ) ? $maxlength : $remaining;
394 my $rc = $self->read_chunk( $c, my $buffer, $readlen );
395 if ( defined $rc ) {
396 $self->read_position( $self->read_position + $rc );
397 return $buffer;
398 }
399 else {
400 Catalyst::Exception->throw(
401 message => "Unknown error reading input: $!"
402 );
403 }
404}
fc7ec1d9 405
fbcc39ad 406=item $self->read_chunk($c, $buffer, $length)
23f9d934 407
fbcc39ad 408Each engine inplements read_chunk as its preferred way of reading a chunk
409of data.
fc7ec1d9 410
fbcc39ad 411=cut
61b1e958 412
fbcc39ad 413sub read_chunk { }
61b1e958 414
fbcc39ad 415=item $self->read_length
ca39d576 416
fbcc39ad 417The length of input data to be read. This is obtained from the Content-Length
418header.
fc7ec1d9 419
fbcc39ad 420=item $self->read_position
fc7ec1d9 421
fbcc39ad 422The amount of input data that has already been read.
63b763c5 423
fbcc39ad 424=item $self->run($c)
63b763c5 425
fbcc39ad 426=cut
fc7ec1d9 427
fbcc39ad 428sub run { }
fc7ec1d9 429
fbcc39ad 430=item $self->write($c, $buffer)
fc7ec1d9 431
432=cut
433
fbcc39ad 434sub write {
435 my ( $self, $c, $buffer ) = @_;
436
437 unless ( $self->{_prepared_write} ) {
438 $self->prepare_write( $c );
439 $self->{_prepared_write} = 1;
fc7ec1d9 440 }
fbcc39ad 441
442 my $handle = $c->response->handle;
443
444 print $handle $buffer;
fc7ec1d9 445}
446
23f9d934 447=back
448
fbcc39ad 449=head1 AUTHORS
450
451Sebastian Riedel, <sri@cpan.org>
fc7ec1d9 452
fbcc39ad 453Andy Grundman, <andy@hybridized.org>
fc7ec1d9 454
455=head1 COPYRIGHT
456
457This program is free software, you can redistribute it and/or modify it under
458the same terms as Perl itself.
459
460=cut
461
4621;