File Coverage

blib/lib/Router/PathInfo/Static.pm
Criterion Covered Total %
statement 48 49 97.9
branch 16 20 80.0
condition 15 27 55.5
subroutine 9 9 100.0
pod 2 2 100.0
total 90 107 84.1


line stmt bran cond sub pod time code
1             package Router::PathInfo::Static;
2 2     2   3768 use strict;
  2         5  
  2         85  
3 2     2   188 use warnings;
  2         5  
  2         117  
4              
5             =head1 NAME
6              
7             B - static routing
8              
9             =head1 DESCRIPTION
10              
11             Class to describe the routing of statics.
12             Allows us to describe the statics as follows:
13              
14             - Specify the starting segment of the URI
15              
16             - Specify a directory on disk, which will host the search for static
17              
18             Statics is divided into two parts:
19              
20             - C - already exists (the "classic") static
21              
22             - C - created on demand
23              
24             Case C it's different css, js, images. C it's archives and another.
25             If the file to C not found, C return undef - a signal that makes sense to continue search of L routing.
26              
27             If successful, returns hashref:
28              
29             {
30             type => 'static',
31             mime_type => $mime_type,
32             file_name => '/path/to/found.static',
33             };
34            
35             This ensures that the file exists, has size, and is readable.
36              
37              
38             If static is not found (for C) an error is returned:
39              
40             {
41             type => 'error',
42             code => 404,
43             desc => sprintf('not found static for PATH_INFO = %s', $env->{PATH_INFO})
44             }
45              
46             If PATH_INFO contains illegal characters (such as C or C)an error is returned:
47              
48             {
49             type => 'error',
50             code => 403,
51             desc => sprintf('forbidden for PATH_INFO = %s', $env->{PATH_INFO})
52             }
53              
54             Return C means that it makes sense to continue search of L routing.
55              
56             =head1 SYNOPSIS
57            
58             my $s = Router::PathInfo::Static->new(
59             # describe simple static
60             allready => {
61             path => $allready_path,
62             first_uri_segment => 'static'
63             },
64             # describe on demand created static
65             on_demand => {
66             path => $on_demand_path,
67             first_uri_segment => 'archives',
68             }
69             );
70            
71             my $env = {PATH_INFO => '/static/some.jpg'};
72             my @segment = split '/', $env->{PATH_INFO}, -1;
73             shift @segment;
74             $env->{'psgix.tmp.RouterPathInfo'} = {
75             segments => [@segment],
76             depth => scalar @segment
77             };
78              
79             my $res = $s->match($env);
80            
81             # $res = {
82             # type => 'static',
83             # file => $path_to_some_jpg,
84             # mime => 'image/jpeg'
85             # }
86              
87             =head1 METHODS
88              
89             =cut
90              
91 2     2   1236 use namespace::autoclean;
  2         45690  
  2         16  
92 2     2   2328 use Plack::MIME;
  2         2443  
  2         117  
93 2     2   15 use File::Spec;
  2         5  
  2         61  
94 2     2   2494 use File::MimeInfo::Magic qw(mimetype);
  2         27187  
  2         1540  
95              
96             =head2 new(allready => {path => $dir, first_uri_segment => $uri_segment}, on_demand => {...})
97              
98             The constructor accepts the description of the statics (allready) and/or static generated on demand (on_demand).
99             Each description is a hashref with the keys C<'path'> (directory path)
100             and C<'first_uri_segment'> (the first segment of a PATH_INFO, which defines namespace for designated purposes).
101              
102             All arguments are optional. If no arguments are given, the object is not created.
103              
104             =cut
105             sub new {
106 2     2 1 1200 my $class = shift;
107 2         10 my %param = @_;
108            
109 2         4 my $hash = {};
110            
111 2         6 for (qw(allready on_demand)) {
112 4         10 my $token = delete $param{$_};
113 4 50       14 if (ref $token) {
114 4 50 33     113 if (-e $token->{path} and -d _ and $token->{first_uri_segment}) {
      33        
115 4         14 $hash->{$_.'_path'} = $token->{path};
116 4         12 $hash->{$_.'_uri_segment'} = $token->{first_uri_segment};
117 4         13 $hash->{$_} = 1;
118             } else {
119 0         0 $hash->{$_} = 0;
120             }
121             }
122             }
123              
124 2 50       27 return keys %$hash ? bless($hash, $class) : undef;
125             }
126              
127             sub _type_uri {
128 12     12   21 my $self = shift;
129 12         17 my $first_segment = shift;
130            
131 12         26 for (qw(allready on_demand)) {
132 18 100 66     404 return $_ if ($self->{$_} and $first_segment eq $self->{$_.'_uri_segment'});
133             }
134            
135 2         4 return;
136             }
137              
138             =head2 match({'psgix.tmp.RouterPathInfo' => {...}})
139              
140             Objects method.
141             Receives a uri and return:
142              
143             For C created static, return undef if file not found.
144              
145             =cut
146             sub match {
147 12     12 1 14397 my $self = shift;
148 12         16 my $env = shift;
149            
150 12         17 my @segment = @{$env->{'psgix.tmp.RouterPathInfo'}->{segments}};
  12         39  
151              
152 12         23 my $serch_file = pop @segment;
153 12 50 33     69 return unless ($serch_file and @segment);
154            
155             # проверим первый сегмент uri на принадлежность к статике
156 12         142 my $type = $self->_type_uri(shift @segment);
157 12 100       38 return unless $type;
158              
159             # среди прочего небольшая защита для никсойдов, дабы не отдать секьюрные файлы
160             return {
161 7 100       73 type => 'error',
162             code => 403,
163             desc => sprintf('forbidden for PATH_INFO = %s', $env->{PATH_INFO})
164 10 100 66     93 } if ($serch_file =~ /^\./ or $serch_file =~ /~/ or grep {$_ =~ /^\./ or $_ =~ /~/} @segment);
      100        
165              
166 7         139 $serch_file = File::Spec->catfile($self->{$type.'_path'}, @segment, $serch_file);
167 7 100 66     373 if (-f $serch_file and -s _ and -r _) {
      66        
168             return {
169 3   33     40 type => 'static',
170             file => $serch_file,
171             mime => Plack::MIME->mime_type($serch_file) || mimetype($serch_file)
172             }
173             } else {
174 4 100       30 return $type eq 'allready' ?
175             {
176             type => 'error',
177             code => 404,
178             desc => sprintf('not found static for PATH_INFO = %s', $env->{PATH_INFO})
179             } :
180             undef;
181             }
182             }
183              
184             =head1 DEPENDENCIES
185              
186             L, L
187              
188             =head1 SEE ALSO
189              
190             L, L
191              
192             =head1 AUTHOR
193              
194             mr.Rico
195              
196             =cut
197             1;
198             __END__