allow the redirect method to accept a URI style object to reduce boilerplate for...
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Response.pm
CommitLineData
fc7ec1d9 1package Catalyst::Response;
2
059c085b 3use Moose;
6680c772 4use HTTP::Headers;
faa02805 5use Moose::Util::TypeConstraints;
6use namespace::autoclean;
b194746d 7use Scalar::Util 'blessed';
fc7ec1d9 8
531f1ab6 9with 'MooseX::Emulate::Class::Accessor::Fast';
10
faa02805 11has _response_cb => (
12 is => 'ro',
46fff667 13 isa => 'CodeRef',
faa02805 14 writer => '_set_response_cb',
15 clearer => '_clear_response_cb',
16 predicate => '_has_response_cb',
17);
18
19subtype 'Catalyst::Engine::Types::Writer',
20 as duck_type([qw(write close)]);
21
22has _writer => (
23 is => 'ro',
46fff667 24 isa => 'Catalyst::Engine::Types::Writer', #Pointless since we control how this is built
25 #writer => '_set_writer', Now that its lazy I think this is safe to remove
faa02805 26 clearer => '_clear_writer',
27 predicate => '_has_writer',
46fff667 28 lazy => 1,
29 builder => '_build_writer',
faa02805 30);
31
46fff667 32sub _build_writer {
33 my $self = shift;
34
35 ## These two lines are probably crap now...
36 $self->_context->finalize_headers unless
37 $self->finalized_headers;
38
39 my @headers;
40 $self->headers->scan(sub { push @headers, @_ });
41
42 my $writer = $self->_response_cb->([ $self->status, \@headers ]);
43 $self->_clear_response_cb;
44
45 return $writer;
46}
47
e37f92f5 48has write_fh => (
49 is=>'ro',
a3c9ab76 50 predicate=>'_has_write_fh',
eb1f4b49 51 lazy=>1,
1f2a8069 52 builder=>'_build_write_fh',
53);
54
46fff667 55sub _build_write_fh { shift ->_writer }
e37f92f5 56
57sub DEMOLISH {
58 my $self = shift;
a3c9ab76 59 return if $self->_has_write_fh;
e37f92f5 60 if($self->_has_writer) {
61 $self->_writer->close
62 }
63}
faa02805 64
6680c772 65has cookies => (is => 'rw', default => sub { {} });
ffb43803 66has body => (is => 'rw', default => undef);
67sub has_body { defined($_[0]->body) }
99a543ee 68
059c085b 69has location => (is => 'rw');
6680c772 70has status => (is => 'rw', default => 200);
71has finalized_headers => (is => 'rw', default => 0);
059c085b 72has headers => (
73 is => 'rw',
9c331634 74 isa => 'HTTP::Headers',
059c085b 75 handles => [qw(content_encoding content_length content_type header)],
6680c772 76 default => sub { HTTP::Headers->new() },
77 required => 1,
78 lazy => 1,
059c085b 79);
258733f1 80has _context => (
81 is => 'rw',
82 weak_ref => 1,
83 clearer => '_clear_context',
84);
fc7ec1d9 85
9ae060f0 86before [qw(status headers content_encoding content_length content_type header)] => sub {
87 my $self = shift;
88
89 $self->_context->log->warn(
90 "Useless setting a header value after finalize_headers called." .
91 " Not what you want." )
92 if ( $self->finalized_headers && @_ );
93};
94
059c085b 95sub output { shift->body(@_) }
96
aa9e8261 97sub code { shift->status(@_) }
98
9c4288ea 99sub write {
100 my ( $self, $buffer ) = @_;
101
102 # Finalize headers if someone manually writes output
89ba65d5 103 $self->_context->finalize_headers unless $self->finalized_headers;
9c4288ea 104
105 $buffer = q[] unless defined $buffer;
106
107 my $len = length($buffer);
108 $self->_writer->write($buffer);
109
110 return $len;
111}
112
9c4288ea 113sub finalize_headers {
114 my ($self) = @_;
9c4288ea 115 return;
116}
117
e67f0874 118sub from_psgi_response {
119 my ($self, $psgi_res) = @_;
b194746d 120 if(blessed($psgi_res) && $psgi_res->can('as_psgi')) {
121 $psgi_res = $psgi_res->as_psgi;
122 }
e67f0874 123 if(ref $psgi_res eq 'ARRAY') {
124 my ($status, $headers, $body) = @$psgi_res;
125 $self->status($status);
4491e0cc 126 $self->headers(HTTP::Headers->new(@$headers));
8a3dcb98 127 $self->body($body);
e67f0874 128 } elsif(ref $psgi_res eq 'CODE') {
129 $psgi_res->(sub {
4491e0cc 130 my $response = shift;
131 my ($status, $headers, $maybe_body) = @$response;
e67f0874 132 $self->status($status);
4491e0cc 133 $self->headers(HTTP::Headers->new(@$headers));
8a3dcb98 134 if(defined $maybe_body) {
135 $self->body($maybe_body);
e67f0874 136 } else {
137 return $self->write_fh;
138 }
4491e0cc 139 });
140 } else {
e67f0874 141 die "You can't set a Catalyst response from that, expect a valid PSGI response";
142 }
143}
144
fc7ec1d9 145=head1 NAME
146
910410b8 147Catalyst::Response - stores output responding to the current client request
fc7ec1d9 148
149=head1 SYNOPSIS
150
fbcc39ad 151 $res = $c->response;
152 $res->body;
aa9e8261 153 $res->code;
fbcc39ad 154 $res->content_encoding;
155 $res->content_length;
156 $res->content_type;
157 $res->cookies;
fbcc39ad 158 $res->header;
159 $res->headers;
160 $res->output;
161 $res->redirect;
162 $res->status;
163 $res->write;
b22c6668 164
fc7ec1d9 165=head1 DESCRIPTION
166
910410b8 167This is the Catalyst Response class, which provides methods for responding to
46372e65 168the current client request. The appropriate L<Catalyst::Engine> for your environment
169will turn the Catalyst::Response into a HTTP Response and return it to the client.
b22c6668 170
171=head1 METHODS
fc7ec1d9 172
08a2c908 173=head2 $res->body( $text | $fh | $iohandle_object )
e060fe05 174
175 $c->response->body('Catalyst rocks!');
06e1b616 176
46372e65 177Sets or returns the output (text or binary data). If you are returning a large body,
2f381252 178you might want to use a L<IO::Handle> type of object (Something that implements the read method
46372e65 179in the same fashion), or a filehandle GLOB. Catalyst
180will write it piece by piece into the response.
06e1b616 181
490b7a80 182When using a L<IO::Handle> type of object and no content length has been
183already set in the response headers Catalyst will make a reasonable attempt
184to determine the size of the Handle. Depending on the implementation of your
185handle object, setting the content length may fail. If it is at all possible
186for you to determine the content length of your handle object,
4a178c0d 187it is recommended that you set the content length in the response headers
490b7a80 188yourself, which will be respected and sent by Catalyst in the response.
189
efeeb257 190Please note that the object needs to implement C<getline>, not just
191C<read>.
192
193Starting from version 5.90060, when using an L<IO::Handle> object, you
194may want to use L<Plack::Middleware::XSendfile>, to delegate the
195actual serving to the frontend server. To do so, you need to pass to
196C<body> an IO object with a C<path> method. This can be achieved in
197two ways.
198
199Either using L<Plack::Util>:
200
201 my $fh = IO::File->new($file, 'r');
202 Plack::Util::set_io_path($fh, $file);
203
204Or using L<IO::File::WithPath>
205
206 my $fh = IO::File::WithPath->new($file, 'r');
207
208And then passing the filehandle to body and setting headers, if needed.
209
210 $c->response->body($fh);
211 $c->response->headers->content_type('text/plain');
212 $c->response->headers->content_length(-s $file);
213 $c->response->headers->last_modified((stat($file))[9]);
214
215L<Plack::Middleware::XSendfile> can be loaded in the application so:
216
217 __PACKAGE__->config(
218 psgi_middleware => [
219 'XSendfile',
220 # other middlewares here...
221 ],
222 );
223
224B<Beware> that loading the middleware without configuring the
225webserver to set the request header C<X-Sendfile-Type> to a supported
226type (C<X-Accel-Redirect> for nginx, C<X-Sendfile> for Apache and
227Lighttpd), could lead to the disclosure of private paths to malicious
228clients setting that header.
229
230Nginx needs the additional X-Accel-Mapping header to be set in the
231webserver configuration, so the middleware will replace the absolute
232path of the IO object with the internal nginx path. This is also
233useful to prevent a buggy app to server random files from the
234filesystem, as it's an internal redirect.
235
236An nginx configuration for FastCGI could look so:
237
238 server {
239 server_name example.com;
240 root /my/app/root;
241 location /private/repo/ {
242 internal;
243 alias /my/app/repo/;
244 }
245 location /private/staging/ {
246 internal;
247 alias /my/app/staging/;
248 }
249 location @proxy {
250 include /etc/nginx/fastcgi_params;
251 fastcgi_param SCRIPT_NAME '';
252 fastcgi_param PATH_INFO $fastcgi_script_name;
253 fastcgi_param HTTP_X_SENDFILE_TYPE X-Accel-Redirect;
254 fastcgi_param HTTP_X_ACCEL_MAPPING /my/app=/private;
255 fastcgi_pass unix:/my/app/run/app.sock;
256 }
257 }
258
259In the example above, passing filehandles with a local path matching
260/my/app/staging or /my/app/repo will be served by nginx. Passing paths
261with other locations will lead to an internal server error.
262
263Setting the body to a filehandle without the C<path> method bypasses
264the middleware completely.
265
266For Apache and Lighttpd, the mapping doesn't apply and setting the
267X-Sendfile-Type is enough.
268
02570318 269=head2 $res->has_body
270
271Predicate which returns true when a body has been set.
272
aa9e8261 273=head2 $res->code
274
275Alias for $res->status.
276
b5ecfcf0 277=head2 $res->content_encoding
b5176d9e 278
910410b8 279Shortcut for $res->headers->content_encoding.
b5176d9e 280
b5ecfcf0 281=head2 $res->content_length
b5176d9e 282
910410b8 283Shortcut for $res->headers->content_length.
b5176d9e 284
b5ecfcf0 285=head2 $res->content_type
b5176d9e 286
910410b8 287Shortcut for $res->headers->content_type.
b5176d9e 288
87e9f9ab 289This value is typically set by your view or plugin. For example,
290L<Catalyst::Plugin::Static::Simple> will guess the mime type based on the file
291it found, while L<Catalyst::View::TT> defaults to C<text/html>.
292
b5ecfcf0 293=head2 $res->cookies
fc7ec1d9 294
910410b8 295Returns a reference to a hash containing cookies to be set. The keys of the
296hash are the cookies' names, and their corresponding values are hash
7e743798 297references used to construct a L<CGI::Simple::Cookie> object.
fc7ec1d9 298
299 $c->response->cookies->{foo} = { value => '123' };
300
7e743798 301The keys of the hash reference on the right correspond to the L<CGI::Simple::Cookie>
910410b8 302parameters of the same name, except they are used without a leading dash.
303Possible parameters are:
ac965e92 304
b0ad47c1 305=over
ac965e92 306
71453caf 307=item value
ac965e92 308
71453caf 309=item expires
ac965e92 310
71453caf 311=item domain
ac965e92 312
71453caf 313=item path
314
315=item secure
316
b21bc468 317=item httponly
318
71453caf 319=back
ac965e92 320
b5ecfcf0 321=head2 $res->header
fbcc39ad 322
910410b8 323Shortcut for $res->headers->header.
fbcc39ad 324
b5ecfcf0 325=head2 $res->headers
fc7ec1d9 326
910410b8 327Returns an L<HTTP::Headers> object, which can be used to set headers.
fc7ec1d9 328
329 $c->response->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
330
b5ecfcf0 331=head2 $res->output
fc7ec1d9 332
910410b8 333Alias for $res->body.
fc7ec1d9 334
b5ecfcf0 335=head2 $res->redirect( $url, $status )
fc7ec1d9 336
2f381252 337Causes the response to redirect to the specified URL. The default status is
338C<302>.
fc7ec1d9 339
73a52566 340 $c->response->redirect( 'http://slashdot.org' );
341 $c->response->redirect( 'http://slashdot.org', 307 );
342
2f381252 343This is a convenience method that sets the Location header to the
344redirect destination, and then sets the response status. You will
ee24f3a8 345want to C< return > or C<< $c->detach() >> to interrupt the normal
2f381252 346processing flow if you want the redirect to occur straight away.
347
824a5eb0 348B<Note:> do not give a relative URL as $url, i.e: one that is not fully
349qualified (= C<http://...>, etc.) or that starts with a slash
350(= C</path/here>). While it may work, it is not guaranteed to do the right
351thing and is not a standard behaviour. You may opt to use uri_for() or
352uri_for_action() instead.
353
00038a21 354B<Note:> If $url is an object that does ->as_string (such as L<URI>, which is
355what you get from ->uri_for) we automatically call that to stringify. This
356should ease the common case usage
357
358 return $c->res->redirect( $c->uri_for(...));
359
73a52566 360=cut
361
362sub redirect {
363 my $self = shift;
fbcc39ad 364
365 if (@_) {
73a52566 366 my $location = shift;
f1bbebac 367 my $status = shift || 302;
73a52566 368
00038a21 369 if(blessed($location) && $location->can('as_string')) {
370 $location = $location->as_string;
371 }
372
73a52566 373 $self->location($location);
374 $self->status($status);
375 }
376
377 return $self->location;
378}
fc7ec1d9 379
059c085b 380=head2 $res->location
381
382Sets or returns the HTTP 'Location'.
383
b5ecfcf0 384=head2 $res->status
fc7ec1d9 385
910410b8 386Sets or returns the HTTP status.
fc7ec1d9 387
388 $c->response->status(404);
aa9e8261 389
390$res->code is an alias for this, to match HTTP::Response->code.
b0ad47c1 391
b5ecfcf0 392=head2 $res->write( $data )
fbcc39ad 393
394Writes $data to the output stream.
395
e37f92f5 396=head2 $res->write_fh
397
398Returns a PSGI $writer object that has two methods, write and close. You can
399close over this object for asynchronous and nonblocking applications. For
400example (assuming you are using a supporting server, like L<Twiggy>
401
402 package AsyncExample::Controller::Root;
403
404 use Moose;
405
406 BEGIN { extends 'Catalyst::Controller' }
407
408 sub prepare_cb {
409 my $write_fh = pop;
410 return sub {
411 my $message = shift;
412 $write_fh->write("Finishing: $message\n");
413 $write_fh->close;
414 };
415 }
416
417 sub anyevent :Local :Args(0) {
418 my ($self, $c) = @_;
419 my $cb = $self->prepare_cb($c->res->write_fh);
420
421 my $watcher;
422 $watcher = AnyEvent->timer(
423 after => 5,
424 cb => sub {
425 $cb->(scalar localtime);
426 undef $watcher; # cancel circular-ref
427 });
428 }
429
e4cc83b2 430=head2 $res->print( @data )
431
432Prints @data to the output stream, separated by $,. This lets you pass
433the response object to functions that want to write to an L<IO::Handle>.
434
8738b8fe 435=head2 $self->finalize_headers($c)
436
437Writes headers to response if not already written
438
e67f0874 439=head2 from_psgi_response
440
441Given a PSGI response (either three element ARRAY reference OR coderef expecting
442a $responder) set the response from it.
443
444Properly supports streaming and delayed response and / or async IO if running
445under an expected event loop.
446
b194746d 447If passed an object, will expect that object to do a method C<as_psgi>.
448
e67f0874 449Example:
450
451 package MyApp::Web::Controller::Test;
452
453 use base 'Catalyst::Controller';
454 use Plack::App::Directory;
455
456
457 my $app = Plack::App::Directory->new({ root => "/path/to/htdocs" })
458 ->to_app;
459
460 sub myaction :Local Args {
461 my ($self, $c) = @_;
faa1bcff 462 $c->res->from_psgi_response($app->($c->req->env));
e67f0874 463 }
464
465Please note this does not attempt to map or nest your PSGI application under
466the Controller and Action namespace or path.
467
faa02805 468=head2 DEMOLISH
469
470Ensures that the response is flushed and closed at the end of the
471request.
472
473=head2 meta
474
475Provided by Moose
476
e4cc83b2 477=cut
478
479sub print {
480 my $self = shift;
481 my $data = shift;
482
483 defined $self->write($data) or return;
484
485 for (@_) {
486 defined $self->write($,) or return;
487 defined $self->write($_) or return;
488 }
fe3083a8 489 defined $self->write($\) or return;
b0ad47c1 490
e4cc83b2 491 return 1;
492}
493
910410b8 494=head1 AUTHORS
fc7ec1d9 495
2f381252 496Catalyst Contributors, see Catalyst.pm
fc7ec1d9 497
498=head1 COPYRIGHT
499
b0ad47c1 500This library is free software. You can redistribute it and/or modify
61b1e958 501it under the same terms as Perl itself.
fc7ec1d9 502
503=cut
504
e5ecd5bc 505__PACKAGE__->meta->make_immutable;
506
fc7ec1d9 5071;