File Coverage

blib/lib/Plack/Middleware/ETag.pm
Criterion Covered Total %
statement 55 56 98.2
branch 17 24 70.8
condition 7 14 50.0
subroutine 9 9 100.0
pod 1 1 100.0
total 89 104 85.5


line stmt bran cond sub pod time code
1             package Plack::Middleware::ETag;
2              
3             # ABSTRACT: Adds automatically an ETag header.
4              
5 3     3   35488 use strict;
  3         7  
  3         137  
6 3     3   17 use warnings;
  3         6  
  3         104  
7 3     3   2223 use Digest::SHA;
  3         5905  
  3         164  
8 3     3   1474 use Plack::Util;
  3         21455  
  3         95  
9             use Plack::Util::Accessor
10 3     3   1068 qw( file_etag cache_control check_last_modified_header);
  3         280  
  3         28  
11              
12             our $VERSION = '0.03';
13              
14 3     3   272 use parent qw/Plack::Middleware/;
  3         7  
  3         24  
15              
16             sub call {
17 8     8 1 76790 my $self = shift;
18 8         55 my $res = $self->app->(@_);
19              
20             $self->response_cb(
21             $res,
22             sub {
23 8     8   152 my $res = shift;
24 8         15 my $headers = $res->[1];
25 8 50       31 return if ( !defined $res->[2] );
26 8 100       30 return if ( Plack::Util::header_exists( $headers, 'ETag' ) );
27             return
28 7 100 66     217 if ( $self->check_last_modified_header()
29             && Plack::Util::header_exists( $headers, 'Last-Modified' ) );
30              
31 6         50 my $etag;
32              
33 6 100       25 if ( Plack::Util::is_real_fh( $res->[2] ) ) {
34              
35 1   50     63 my $file_attr = $self->file_etag || [qw/inode mtime size/];
36 1         30 my @stats = stat $res->[2];
37 1 50       11 if ( $stats[9] == time - 1 ) {
38              
39             # if the file was modified less than one second before the request
40             # it may be modified in a near future, so we return a weak etag
41 0         0 $etag = "W/";
42             }
43 1 50       2 if ( grep {/inode/} @$file_attr ) {
  3         11  
44 1         6 $etag .= ( sprintf "%x", $stats[2] );
45             }
46 1 50       2 if ( grep {/mtime/} @$file_attr ) {
  3         10  
47 1 50 33     10 $etag .= "-" if ( $etag && $etag !~ /-$/ );
48 1         4 $etag .= ( sprintf "%x", $stats[9] );
49             }
50 1 50       3 if ( grep {/size/} @$file_attr ) {
  3         11  
51 1 50 33     9 $etag .= "-" if ( $etag && $etag !~ /-$/ );
52 1         5 $etag .= ( sprintf "%x", $stats[7] );
53             }
54             } else {
55 5         211 my $sha = Digest::SHA->new;
56 5         89 $sha->add( @{ $res->[2] } );
  5         26  
57 5         60 $etag = $sha->hexdigest;
58             }
59 6         82 Plack::Util::header_set( $headers, 'ETag', $etag );
60 6         279 $self->_set_cache_control($headers);
61 6         100 return;
62             }
63 8         344 );
64             }
65              
66             sub _set_cache_control {
67 6     6   19 my ( $self, $headers ) = @_;
68 6 100       23 return unless $self->cache_control;
69              
70 2 100 66     174 if ( ref $self->cache_control && ref $self->cache_control eq 'ARRAY' ) {
71 1         4 Plack::Util::header_set( $headers, 'Cache-Control',
72 1         19 join( ', ', @{ $self->cache_control } ) );
73             } else {
74 1         13 Plack::Util::header_set( $headers, 'Cache-Control',
75             'must-revalidate' );
76             }
77             }
78              
79             1;
80              
81              
82             =pod
83              
84             =head1 NAME
85              
86             Plack::Middleware::ETag - Adds automatically an ETag header.
87              
88             =head1 VERSION
89              
90             version 0.03
91              
92             =head1 SYNOPSIS
93              
94             use Plack::Builder;
95              
96             my $app = builder {
97             enable "Plack::Middleware::ETag", file_etag => [qw/inode mtime size/];
98             sub {['200', ['Content-Type' => 'text/html'}, ['hello world']]};
99             };
100              
101             =head1 DESCRIPTION
102              
103             Plack::Middleware::ETag adds automatically an ETag header. You may want to use it with C.
104              
105             my $app = builder {
106             enable "Plack::Middleware::ConditionalGET";
107             enable "Plack::Middleware::ETag", file_etag => "inode";
108             sub {['200', ['Content-Type' => 'text/html'}, ['hello world']]};
109             };
110              
111             =head2 CONFIGURATION
112              
113             =over 4
114              
115             =item file_etag
116              
117             If the content is a file handle, the ETag will be set using the inode, modified time and the file size. You can select which attributes of the file will be used to set the ETag:
118              
119             enable "Plack::Middleware::ETag", file_etag => [qw/size/];
120              
121             =item cache_control
122              
123             It's possible to add 'Cache-Control' header.
124              
125             enable "Plack::Middleware::ETag", cache_control => 1;
126              
127             Will add "Cache-Control: must-revalidate" to the headers.
128              
129             enable "Plack::Middleware::ETag", cache_control => [ 'must-revalidate', 'max-age=3600' ];
130              
131             Will add "Cache-Control: must-revalidate, max-age=3600" to the headers.
132              
133             =item check_last_modified_header
134              
135             Will not add an ETag if there is already a Last-Modified header.
136              
137             =back
138              
139             =head1 AUTHOR
140              
141             franck cuny
142              
143             =head1 COPYRIGHT AND LICENSE
144              
145             This software is copyright (c) 2011 by franck cuny.
146              
147             This is free software; you can redistribute it and/or modify it under
148             the same terms as the Perl 5 programming language system itself.
149              
150             =cut
151              
152              
153             __END__