File Coverage

blib/lib/Perlbal/Plugin/Expires.pm
Criterion Covered Total %
statement 52 66 78.7
branch 14 22 63.6
condition 15 22 68.1
subroutine 11 14 78.5
pod 0 5 0.0
total 92 129 71.3


line stmt bran cond sub pod time code
1             package Perlbal::Plugin::Expires;
2              
3 3     3   255675 use strict;
  3         9  
  3         122  
4 3     3   18 use warnings;
  3         6  
  3         161  
5              
6             our $VERSION = '0.02';
7              
8 3     3   2270 use Perlbal;
  3         721695  
  3         90  
9 3     3   28 use HTTP::Date;
  3         6  
  3         3603  
10              
11             sub load {
12 1     1 0 102 my $class = shift;
13              
14 1         6 Perlbal::register_global_hook('manage_command.expires' => \&_config_expires);
15              
16 1         25 return 1;
17             }
18              
19             sub register {
20 1     1 0 5824 my $class = shift;
21 1         3 my ($svc) = @_;
22              
23 1 50 33     37 die "Expires plugin must run as web_server role\n"
24             unless $svc && $svc->{role} eq 'web_server';
25              
26             $svc->register_hook(
27 3     3   303496 'Expires', 'modify_response_headers', sub { _set_expires($svc, @_) },
28 1         9 );
29              
30 1         19 return 1;
31             }
32              
33             sub unload {
34 0     0 0 0 my $class = shift;
35            
36 0         0 Perlbal::unregister_global_hook('manage_command.expires');
37              
38 0         0 return 1;
39             }
40              
41             sub unregister {
42 0     0 0 0 my $class = shift;
43 0         0 my ($svc) = @_;
44              
45 0         0 $svc->unregister_hooks('Expires');
46              
47 0         0 return 1;
48             }
49              
50             sub _config_expires {
51 5     5   6270 my $mc = shift->parse(
52             qr{^expires\s+(\w+)?\s*(default|[\w\-]+/[\w\-]+)\s*=\s*(\w+)\s+plus\s+(.+)$},
53             "usage: Expires [service] = plus ( )+",
54             );
55 5         190 my ($service, $type, $base, $expires) = $mc->args;
56 5   66     220 $service ||= $mc->{ctx}{last_created};
57              
58 5 100 100     50 return $mc->err("unknown base time string: $base")
      100        
59             unless $base eq 'access' || $base eq 'now' || $base eq 'modification';
60              
61 4 100       93 my $sec = eval { _expires_to_sec($expires) }
  4         12  
62             or return $mc->err($@);
63              
64 2         8 my $svc = Perlbal->service($service);
65 2   100     18 my $config = $svc->{extra_config}->{__expires} ||= {};
66 2         9 $config->{$type} = {
67             base => $base,
68             time => $sec,
69             orig => $expires,
70             };
71              
72 2         7 return $mc->ok;
73             }
74              
75             sub _set_expires {
76 3     3   7 my Perlbal::Service $svc = shift;
77 3         6 my Perlbal::ClientHTTPBase $client = shift;
78 3 50       18 my Perlbal::HTTPHeaders $res = $client->{res_headers} or return;
79              
80 3 50       22 return if $res->response_code ne '200';
81 3 50       37 return unless exists $svc->{extra_config}{__expires};
82              
83 3   50     12 my $type = $res->header('Content-Type') || 'default';
84 3         29 my $config = $svc->{extra_config}{__expires};
85 3 50 66     28 my $expires = $config->{$type} || $config->{default}
86             or return;
87              
88 3         14 my $base = _base_time($expires->{base}, $res->header('Last-Modified'));
89 3         22 $res->header('Expires', HTTP::Date::time2str($base + $expires->{time}));
90              
91 3         78 0;
92             }
93              
94             sub _base_time {
95 3     3   25 my ($type, $last_modified) = @_;
96              
97 3 50 33     32 return ($type eq 'modification' && $last_modified)
98             ? HTTP::Date::str2time($last_modified)
99             : time
100             ;
101             }
102              
103             my %__unit2sec = (
104             years => 365 * 24 * 60 * 60,
105             year => 365 * 24 * 60 * 60,
106             months => 31 * 24 * 60 * 60,
107             month => 31 * 24 * 60 * 60,
108             weeks => 7 * 24 * 60 * 60,
109             week => 7 * 24 * 60 * 60,
110             days => 24 * 60 * 60,
111             day => 24 * 60 * 60,
112             hours => 60 * 60,
113             hour => 60 * 60,
114             minutes => 60,
115             minute => 60,
116             seconds => 1,
117             second => 1,
118             );
119              
120             sub _expires_to_sec {
121 7     7   831 my ($expires) = @_;
122              
123 7         12 my $sec = 0;
124 7         30 my @a = split /\s+/, $expires;
125 7         36 while (my ($num, $unit) = splice @a, 0, 2) {
126 10 100       58 die "can't parse expires string: $expires\n"
127             unless $num =~ /^\d+$/;
128 9 100       57 die "unknown time unit '$unit' in '$expires'\n"
129             unless exists $__unit2sec{$unit};
130 8         38 $sec += $num * $__unit2sec{$unit};
131             }
132              
133 5         15 return $sec;
134             }
135              
136             sub dumpconfig {
137 0     0 0   my $class = shift;
138 0           my ($svc) = @_;
139              
140 0 0         my $expires = $svc->{extra_config}->{__expires} or return;
141              
142 0           my @config;
143 0           while (my ($type, $expire) = each %$expires) {
144 0           push @config, sprintf(qq{Expires $type = %s plug %s}, $expire->{base}, $expire->{orig});
145             }
146              
147 0           return @config;
148             }
149              
150             1;
151             __END__