File Coverage

blib/lib/Catalyst/Plugin/Static/File.pm
Criterion Covered Total %
statement 43 43 100.0
branch 2 2 100.0
condition 2 3 66.6
subroutine 12 12 100.0
pod 1 1 100.0
total 60 61 98.3


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::Static::File;
2              
3 5     5   11657618 use v5.14;
  5         78  
4              
5             # ABSTRACT: Serve a specific static file
6              
7 5     5   39 use Moose::Role;
  5         11  
  5         71  
8              
9 5     5   33204 use File::Spec;
  5         24  
  5         168  
10 5     5   38 use File::stat;
  5         15  
  5         66  
11 5     5   383 use IO::File;
  5         17  
  5         830  
12 5     5   2175 use Plack::MIME;
  5         3458  
  5         218  
13 5     5   45 use Plack::Util;
  5         18  
  5         157  
14 5     5   33 use Try::Tiny;
  5         13  
  5         286  
15              
16 5     5   39 use namespace::autoclean;
  5         33  
  5         38  
17              
18             our $VERSION = 'v0.2.0';
19              
20              
21             sub serve_static_file {
22 8     8 1 380559 my ( $c, $path, $type ) = @_;
23              
24 8         73 my $res = $c->res;
25              
26 8         698 my $abs = File::Spec->rel2abs("$path");
27              
28             try {
29              
30 8 100   8   791 my $fh = IO::File->new( $abs, "r" ) or die $!;
31              
32 7         1222 binmode($fh);
33 7         75 Plack::Util::set_io_path( $fh, $abs );
34 7         500 $res->body($fh);
35              
36 7   66     256 $type //= Plack::MIME->mime_type($abs);
37              
38 7         201 my $headers = $res->headers;
39 7         1405 $headers->content_type("$type");
40              
41 7         132 my $stat = stat($fh);
42 7         1694 $headers->content_length( $stat->size );
43 7         461 $headers->last_modified( $stat->mtime );
44              
45             }
46             catch {
47              
48 1     1   157 my $error = $_;
49 1         21 Catalyst::Exception->throw("Unable to open ${abs} for reading: ${error}");
50              
51 8         163 };
52              
53 7         6229 return 1;
54             }
55              
56              
57             1;
58              
59             __END__
60              
61             =pod
62              
63             =encoding UTF-8
64              
65             =head1 NAME
66              
67             Catalyst::Plugin::Static::File - Serve a specific static file
68              
69             =head1 VERSION
70              
71             version v0.2.0
72              
73             =head1 SYNOPSIS
74              
75             In your Catalyst class:
76              
77             use Catalyst qw/
78             Static::File
79             /;
80              
81             In a controller method:
82              
83             $c->serve_static_file( $absolute_path, $type );
84              
85             =head1 DESCRIPTION
86              
87             This plugin provides a simple method for your L<Catalyst> app to send a specific static file.
88              
89             Unlike L<Catalyst::Plugin::Static::Simple>,
90              
91             =over
92              
93             =item *
94              
95             It only supports serving a single file, not a directory of static files. Use L<Plack::Middleware::Static> if you want to
96             serve multiple files.
97              
98             =item *
99              
100             It assumes that you know what you're doing. If the file does not exist, it will throw an fatal error.
101              
102             =item *
103              
104             It uses L<Plack::MIME> to identify the content type, but you can override that.
105              
106             =item *
107              
108             It adds a file path to the file handle, plays nicely with L<Plack::Middleware::XSendfile> and L<Plack::Middleware::ETag>.
109              
110             =item *
111              
112             It does not log anything.
113              
114             =back
115              
116             =head1 METHODS
117              
118             =head2 serve_static_file
119              
120             $c->serve_static_file( $absolute_path, $type );
121              
122             This serves the file in C<$absolute_path>, with the C<$type> content type.
123              
124             If the C<$type> is omitted, it will guess the type using the filename.
125              
126             It will also set the C<Last-Modified> and C<Content-Length> headers.
127              
128             It returns a true value on success.
129              
130             If you want to use conditional requests, use L<Plack::Middleware::ConditionalGET>.
131              
132             =head1 SECURITY
133              
134             The L<serve_static_file> method does not validate the file that is passed to it.
135              
136             You should ensure that arbitrary filenames are not passed to it. You should strictly validate any external data that is
137             used for generating the filename.
138              
139             =head1 SUPPORT FOR OLDER PERL VERSIONS
140              
141             This module requires Perl v5.14 or later.
142              
143             Future releases may only support Perl versions released in the last ten years.
144              
145             =head1 SEE ALSO
146              
147             L<Catalyst>
148              
149             L<Catalyst::Plugin::Static::Simple>
150              
151             =head1 SOURCE
152              
153             The development version is on github at L<https://github.com/robrwo/Catalyst-Plugin-Static-File>
154             and may be cloned from L<git://github.com/robrwo/Catalyst-Plugin-Static-File.git>
155              
156             =head1 BUGS
157              
158             Please report any bugs or feature requests on the bugtracker website
159             L<https://github.com/robrwo/Catalyst-Plugin-Static-File/issues>
160              
161             When submitting a bug or request, please include a test-file or a
162             patch to an existing test-file that illustrates the bug or desired
163             feature.
164              
165             =head1 AUTHOR
166              
167             Robert Rothenberg <rrwo@cpan.org>
168              
169             =head1 COPYRIGHT AND LICENSE
170              
171             This software is Copyright (c) 2023 by Robert Rothenberg.
172              
173             This is free software, licensed under:
174              
175             The Artistic License 2.0 (GPL Compatible)
176              
177             =cut