File Coverage

blib/lib/Plack/App/File/CaseInsensitive.pm
Criterion Covered Total %
statement 47 54 87.0
branch 13 24 54.1
condition 7 13 53.8
subroutine 8 8 100.0
pod 0 1 0.0
total 75 100 75.0


line stmt bran cond sub pod time code
1             package Plack::App::File::CaseInsensitive;
2             {
3             $Plack::App::File::CaseInsensitive::VERSION = '0.001000';
4             }
5              
6             # ABSTRACT: Serve static files with case insensitive paths
7              
8 2     2   807431 use strict;
  2         6  
  2         88  
9 2     2   13 use warnings;
  2         4  
  2         129  
10              
11 2     2   13 use base 'Plack::App::File';
  2         10  
  2         1962  
12              
13 2     2   70988 use mro 'c3';
  2         1964  
  2         15  
14 2     2   97 use File::Find;
  2         4  
  2         3292  
15              
16             sub locate_file {
17 1     1 0 37948 my ($self, $env) = @_;
18              
19             {
20 1         2 my ($file, $path_info) = $self->next::method($env);
  1         8  
21              
22 1 50 33     115 return ($file, $path_info)
      33        
23             unless ref $file && ref $file eq 'ARRAY' && $file->[0] == 404;
24             }
25              
26 1         13 warn "Case insensitive mode! Beware performance problems! ($env->{PATH_INFO})";
27              
28             # the rest of this is more or less copied verbatim from the base class
29 1   50     10 my $path = $env->{PATH_INFO} || '';
30              
31 1 50       5 if ($path =~ /\0/) {
32 0         0 return $self->return_400;
33             }
34              
35 1   50     4 my $docroot = $self->root || ".";
36 1         309 my @path = split /[\\\/]/, $path;
37 1 50       5 if (@path) {
38 1 50       4 shift @path if $path[0] eq '';
39             } else {
40 0         0 @path = ('.');
41             }
42              
43 1 50       25 if (grep $_ eq '..', @path) {
44 0         0 return $self->return_403;
45             }
46              
47 1         2 my ($file, @path_info);
48             OUTER:
49 1         4 while (@path) {
50 1         5 for my $try ($self->_find_file($docroot, @path)) {
51 1 50       7 if ($self->should_handle($try)) {
    0          
52 1         20 $file = $try;
53 1         3 last OUTER;
54             } elsif (!$self->allow_path_info) {
55 0         0 last OUTER;
56             }
57             }
58 0         0 unshift @path_info, pop @path;
59             }
60              
61 1 50       5 if (!$file) {
62 0         0 return $self->return_404;
63             }
64              
65 1 50       14 if (!-r $file) {
66 0         0 return $self->return_403;
67             }
68              
69 1         5 return $file, join("/", "", @path_info);
70             }
71              
72             sub _find_file {
73 1     1   4 my ($self, $docroot, @path) = @_;
74              
75 1         4 my $full_path = join '/', $docroot, @path;
76 1         25 my $re = qr/\Q$full_path\E/i;
77              
78 1         3 my @files;
79             find(sub {
80 22 100 100 22   276 if(-d $File::Find::name && $full_path !~ /^\Q$File::Find::name/i) {
81 3         5 $File::Find::prune = 1;
82 3         49 return;
83             }
84 19 100       609 push @files, $File::Find::name
85             if $File::Find::name =~ $re
86 1         103 }, $docroot);
87              
88 1 50       8 warn "multiple files found!" if @files > 1;
89              
90 1         100 return @files;
91             }
92              
93             1;
94              
95             __END__