File Coverage

blib/lib/Catalyst/Plugin/XSendFile.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::XSendFile;
2 2     2   4004 use strict;
  2         4  
  2         90  
3 2     2   13 use warnings;
  2         5  
  2         168  
4 2     2   11 use base qw/Class::Data::Inheritable/;
  2         3  
  2         2551  
5              
6 2     2   1518 use Catalyst::Utils;
  0            
  0            
7             use File::Temp qw/tempdir tempfile/;
8             use File::stat;
9             use NEXT;
10             use Path::Class qw/file/;
11             use Scalar::Util qw/blessed/;
12             use bytes;
13              
14             our $VERSION = '0.03_001';
15              
16             =head1 NAME
17              
18             Catalyst::Plugin::XSendFile - Catalyst plugin for lighttpd's X-Sendfile.
19              
20             =head1 SYNOPSIS
21              
22             use Catalyst qw/XSendFile/;
23            
24             # manual send file
25             sub show : Path('/files') {
26             my ( $self, $c, $filename ) = @_;
27            
28             # unless login, it shows 403 forbidden screen
29             $c->res->status(403);
30             $c->stash->{template} = 'error-403.tt';
31            
32             # serving a static file only when user logged in.
33             if ($c->user) {
34             $c->res->sendfile( "/path/to/$filename" );
35             }
36             }
37            
38            
39             # auto using x-send-tempfile on large content serving
40             MyApp->config->{sendfile}{tempdir} = '/dev/shm';
41              
42             =head1 NOTICE
43              
44             B<This developer version of module requires lighttpd 1.5.0 (r1477) or above.>
45              
46             =head1 DESCRIPTION
47              
48             lighty's X-Sendfile feature is great.
49              
50             If you use lighttpd + fastcgi, you can show files only set X-Sendfile header like below:
51              
52             $c->res->header( 'X-LIGHTTPD-send-file' => $filename );
53              
54             This feature is especially great for serving static file on authentication area.
55              
56             And with this plugin, you can use:
57              
58             $c->res->sendfile( $filename );
59              
60             instead of above.
61              
62             But off-course you know, this feature doesn't work on Catalyst Test Server (myapp_server.pl).
63             So this module also provide its emulation when your app on test server.
64              
65             =head1 SERVE LARGE CONTENT BY X-LIGHTTPD-send-tempfile
66              
67             Latest version of lighttpd (1.5.0) also support X-LIGHTTPD-send-tempfile, that is almost same to X-LIGHTTPD-send-file except deleting sending file when server sent file.
68              
69             This module automatically use this feature when content length is above 16kbytes.
70              
71             And for more performance, you need to set tempdir ($c->config->{sendfile}{tempdir}) on tmpfs (/dev/shm).
72              
73             See below urls for detail.
74              
75             =head1 SEE ALSO
76              
77             lighty's life - X-Sendfile
78             http://blog.lighttpd.net/articles/2006/07/02/x-sendfile
79              
80             Faster - FastCGI
81             http://blog.lighttpd.net/articles/2006/11/29/faster-fastcgi
82              
83             =head1 NOTICE
84              
85             To use it you have to set "allow-x-sendfile" option enabled in your fastcgi configuration.
86              
87             "allow-x-send-file" => "enable",
88              
89             or on 1.5.0:
90              
91             proxy-core.allow-x-sendfile = "enable"
92              
93             =head1 EXTENDED_METHODS
94              
95             =head2 setup
96              
97             Setup tempdir for x-send-tempfile
98              
99             =cut
100              
101             sub setup {
102             my $c = shift;
103             $c->NEXT::setup(@_);
104              
105             my $tempdir = $c->config->{sendfile}{tempdir}
106             || Catalyst::Utils::class2tempdir($c, 1);
107              
108             __PACKAGE__->mk_classdata(
109             _sendfile_tempdir => tempdir( DIR => $tempdir, CLEANUP => 1 ) );
110              
111             $c;
112             }
113              
114             =head2 finalize_headers
115              
116             Serving large (16kbytes) content via X-LIGHTTPD-send-tempfile.
117              
118             =cut
119              
120             sub finalize_headers {
121             my $c = shift;
122              
123             my $engine = $ENV{CATALYST_ENGINE} || '';
124              
125             # X-Sendfile emulation for test server.
126             if ( $engine =~ /^HTTP/ ) {
127             if ( my $sendfile = file( $c->res->header('X-LIGHTTPD-send-file') ) ) {
128             $c->res->headers->remove_header('X-LIGHTTPD-send-file');
129             if ( $sendfile->stat && -f _ && -r _ ) {
130             $c->res->body( $sendfile->openr );
131             }
132             }
133             }
134             elsif ( $engine eq 'FastCGI' ) {
135              
136             if ( my $body = $c->res->body ) {
137             my ( $fh, $tempfile ) = tempfile( DIR => $c->_sendfile_tempdir );
138              
139             if ( blessed($body) && $body->can('read') or ref($body) eq 'GLOB' ) {
140             my $stat = stat $body;
141             if ( $stat and $stat->size >= 16*1024 ) {
142             while ( !eof $body ) {
143             read $body, my ($buffer), 4096;
144             last unless $fh->write($buffer);
145             }
146             close $body;
147             close $fh;
148              
149             $c->res->send_tempfile($tempfile);
150             }
151             }
152             elsif ( bytes::length($body) >= 16*1024 ) {
153             $fh->write($body);
154             $fh->close;
155              
156             $c->res->send_tempfile($tempfile);
157             }
158             }
159             }
160              
161             $c->NEXT::finalize_headers;
162             }
163              
164             =head1 EXTENDED_RESPONSE_METHODS
165              
166             =head2 sendfile
167              
168             Set X-LIGHTTPD-send-file header easily.
169              
170             =cut
171              
172             {
173             package # avoid PAUSE Indexer
174             Catalyst::Response;
175              
176             sub sendfile {
177             my ($self, $file) = @_;
178             $self->{body} = '';
179             $self->header( 'X-LIGHTTPD-send-file' => $file );
180             }
181              
182             sub send_tempfile {
183             my ($self, $file) = @_;
184             $self->{body} = '';
185             $self->header( 'X-LIGHTTPD-send-tempfile' => $file );
186             }
187             }
188              
189             =head1 AUTHOR
190              
191             Daisuke Murase <typester@cpan.org>
192              
193             =head1 COPYRIGHT
194              
195             This program is free software; you can redistribute
196             it and/or modify it under the same terms as Perl itself.
197              
198             The full text of the license can be found in the
199             LICENSE file included with this module.
200              
201             =cut
202              
203             1;