File Coverage

blib/lib/App/FakeCDN.pm
Criterion Covered Total %
statement 24 70 34.2
branch 0 18 0.0
condition 0 20 0.0
subroutine 8 17 47.0
pod 0 8 0.0
total 32 133 24.0


line stmt bran cond sub pod time code
1             package App::FakeCDN;
2 1     1   954 use 5.010001;
  1         4  
  1         42  
3 1     1   5 use strict;
  1         2  
  1         35  
4 1     1   17 use warnings;
  1         2  
  1         52  
5              
6             our $VERSION = "0.01";
7              
8 1     1   1270 use Path::Tiny;
  1         14262  
  1         71  
9 1     1   721 use Plack::MIME;
  1         1270  
  1         31  
10              
11 1     1   745 use Mouse;
  1         30125  
  1         6  
12 1     1   302 use Mouse::Util::TypeConstraints;
  1         2  
  1         4  
13              
14             subtype 'App::FakeCDN::Path' => as class_type('Path::Tiny');
15             coerce 'App::FakeCDN::Path'
16             => from 'Str'
17             => via { Path::Tiny::path($_) };
18              
19             has cache => (
20             is => 'ro',
21             isa => 'Object',
22             default => sub {
23             require Cache::Memory::Simple;
24             Cache::Memory::Simple->new;
25             },
26             );
27              
28             has root => (
29             is => 'ro',
30             isa => 'App::FakeCDN::Path',
31             required => 1,
32             coerce => 1,
33             );
34              
35             has expiration => (
36             is => 'ro',
37             isa => 'Int',
38             );
39              
40 1     1   161 no Mouse;
  1         2  
  1         6  
41              
42             sub to_app {
43 0     0 0   my $self = shift;
44              
45             sub {
46 0     0     my $env = shift;
47              
48 0   0       my $path = $env->{PATH_INFO} // '';
49 0   0       my $query = $env->{QUERY_STRING} // '';
50              
51 0 0 0       if ($path =~ /\0/ || $query =~ /\0/) {
52 0           return $self->res_400;
53             }
54 0           $path =~ s!^/!!;
55              
56 0           my ($data, $content_type) = $self->get_content($path, $query);
57              
58 0 0         return $self->res_404 unless $data;
59              
60 0           return [ 200, [
61             'Content-Type' => $content_type,
62             'Content-Length' => length($data),
63             ], [ $data ] ];
64 0           };
65             }
66              
67             sub res_400 {
68 0     0 0   [400, ['Content-Type' => 'text/plain', 'Content-Length' => 11], ['Bad Request']];
69             }
70              
71             sub res_404 {
72 0     0 0   [404, ['Content-Type' => 'text/plain', 'Content-Length' => 9], ['not found']];
73             }
74              
75             sub get_content {
76 0     0 0   my ($self, $path, $query) = @_;
77              
78 0   0       my $mime_type = Plack::MIME->mime_type($path) // 'application/octet-stream';
79 0           my $is_binary = is_binary($mime_type);
80              
81 0 0         unless ($is_binary) {
82 0   0       my $encoding = $self->encoding || 'utf-8';
83 0           $mime_type .= "; charset=$encoding";
84             }
85              
86 0           my $content = $self->get_stuff($path, $query);
87              
88 0           ($content, $mime_type);
89             }
90              
91             sub is_binary {
92 0     0 0   my $mime_type = shift;
93              
94 0           $mime_type !~ /\b(?:text|xml|javascript|json)\b/;
95             }
96              
97             sub get_stuff {
98 0     0 0   my ($self, $path, $query) = @_;
99 0           my $key = $path . $query;
100              
101 0 0         if (my $val = $self->cache->get($key)) {
102 0           return $val;
103             }
104             else {
105 0           my $file = $self->root->child($path);
106 0 0         return unless -e -f $file;
107              
108 0           my $val = $file->slurp;
109 0   0       $self->cache->set($key, $val, $self->expiration || ());
110 0           return $val;
111             }
112             }
113              
114             sub parse_options {
115 0     0 0   my ($class, @argv) = @_;
116              
117 0           require Getopt::Long;
118 0           require Pod::Usage;
119              
120 0           my $p = Getopt::Long::Parser->new(
121             config => [qw/posix_default no_ignore_case auto_help pass_through bundling/]
122             );
123 0 0         $p->getoptionsfromarray(\@argv, \my %opt, qw/
124             root=s
125             expiration=i
126             /) or Pod::Usage::pod2usage();
127 0 0         Pod::Usage::pod2usage() if !$opt{root};
128              
129 0           (\%opt, \@argv);
130             }
131              
132             sub run {
133 0     0 0   my $self = shift;
134 0 0         my %args = @_ == 1 ? %{$_[0]} : @_;
  0            
135 0 0 0       if (!$args{listen} && !$args{port} && !$ENV{SERVER_STARTER_PORT}) {
      0        
136 0           $args{port} = 4907;
137             }
138 0           require Plack::Loader;
139 0           Plack::Loader->auto(%args)->run($self->to_app);
140             }
141              
142             1;
143             __END__