File Coverage

blib/lib/Dancer2/Plugin/HTTP/Caching.pm
Criterion Covered Total %
statement 15 36 41.6
branch 0 12 0.0
condition 0 2 0.0
subroutine 5 9 55.5
pod n/a
total 20 59 33.9


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::HTTP::Caching;
2              
3             =head1 NAME
4              
5             Dancer2::Plugin::HTTP::Caching - RFC 7234 compliant
6              
7             =head1 VERSION
8              
9             Version 0.01
10              
11             =cut
12              
13             our $VERSION = '0.01';
14              
15 1     1   20800 use warnings;
  1         3  
  1         28  
16 1     1   5 use strict;
  1         1  
  1         21  
17              
18 1     1   5 use Carp;
  1         5  
  1         71  
19 1     1   845 use Dancer2::Plugin;
  1         3388126  
  1         6  
20              
21 1     1   1200 use HTTP::Date;
  1         4185  
  1         897  
22              
23             =head1 SYNOPSIS
24              
25             Setting the HTTP response headers 'Expire' and 'Cache-Control' according to
26             RFC 7234
27              
28            
29             use Dancer2;
30             use Dancer2::Plugin::HTTP::Caching;
31            
32             get '/aging' => sub {
33             http_cache_max_age 3600; # one hour
34             http_cache_private;
35             http_cache_must_revalidate;
36             http_cache_no_cache 'Set-Cookie';
37             http_cache_no_cache 'WWW-Authenticate';
38             http_expire 'Thu, 31 Dec 2015 23:23:59 GMT';
39            
40             "This content must be refreshed within 1 Hour\"
41             };
42            
43              
44             =head1 RFC_7234 HTTP: Caching
45              
46             That RFC describes a lot on how to store and respond with cached data. But
47             basically, to make caching work it falls in two parts:
48              
49             1) A origin server that SHOULD provide a expiration-date and or directives that
50             tell the cache long it can hold the data. That is basically enough for
51             web-server to do, sending off information about freshness.
52              
53             2) A caching server that once in a while checks with the origin server what to
54             do with it's cached-data, a process called validation. Validation is handled by
55             conditional-requests using request headers like 'If-Modified-Since' and when
56             not, the server SHOULD send a status of 304 (Not Modified).
57              
58             Handling conditional requests by the server is beyond the scope of this caching
59             plugin, and is not described as such in the RFC. For this to work, use the
60             Dancer2::Plugin::HTTP-ConditionalRequest
61              
62             Maybe in a future release there might be a option to have a cache run inside the
63             Dancer app, but if one wants a cache in the origin server, one could simply use
64             plack middle ware that will implement it (Although there is not even one module
65             on CPAN that actually does it right)
66              
67             =head1 Dancer2 Keywords
68              
69             No further explenation is given, see the RFC itself.
70              
71             =cut
72              
73             =head2 http_cache_must_revalidate
74              
75             see RFC 7234: ¤ 5.2.2.1. must-revalidate
76              
77             =cut
78              
79             # RFC 7234: ¤ 5.2.2.1. must-revalidate
80             register http_cache_must_revalidate => sub {
81             shift->_append_cache_control_directive('must-revalidate' => @_);
82             return;
83             };
84              
85             =head2 http_cache_no_cache
86              
87             see RFC 7234: ¤ 5.2.2.2. no-cache
88              
89             takes a string, or a list of strings of HTTP-Response Header-fields
90              
91             =cut
92              
93             # RFC 7234: ¤ 5.2.2.2. no-cache
94             register http_cache_no_cache => sub {
95             shift->_append_cache_control_directive_quoted('no-cache' => @_);
96             return;
97             };
98              
99             =head2 http_cache_no_store
100              
101             see RFC 7234: ¤ 5.2.2.3. no-store
102              
103             =cut
104              
105             # RFC 7234: ¤ 5.2.2.3. no-store
106             register http_cache_no_store => sub {
107             shift->_append_cache_control_directive('no-store' => @_);
108             return;
109             };
110              
111             =head2 http_cache_no_transform
112              
113             see RFC 7234: ¤ 5.2.2.4. no-transform
114              
115             =cut
116              
117             # RFC 7234: ¤ 5.2.2.4. no-transform
118             register http_cache_no_transform => sub {
119             shift->_append_cache_control_directive('no-transform' => @_);
120             return;
121             };
122              
123             =head2 http_cache_public
124              
125             see RFC 7234: ¤ 5.2.2.5. public
126              
127             =cut
128              
129             # RFC 7234: ¤ 5.2.2.5. public
130             register http_cache_public => sub {
131             shift->_append_cache_control_directive('public' => @_);
132             return;
133             };
134              
135             =head2 http_cache_private
136              
137             see RFC 7234: ¤ 5.2.2.6. private
138              
139             takes a string, or a list of strings of HTTP-Response Header-fields
140              
141             =cut
142              
143             # RFC 7234: ¤ 5.2.2.6. private
144             register http_cache_private => sub {
145             shift->_append_cache_control_directive_quoted('private' => @_);
146             return;
147             };
148              
149             =head2 http_cache_proxy_revalidate
150              
151             see RFC 7234: ¤ 5.2.2.7. proxy-revalidate
152              
153             =cut
154              
155             # RFC 7234: ¤ 5.2.2.7. proxy-revalidate
156             register http_cache_proxy_revalidate => sub {
157             shift->_append_cache_control_directive('proxy-revalidate' => @_);
158             return;
159             };
160              
161             =head2 http_cache_max_age
162              
163             see RFC 7234: ¤ 5.2.2.8. max-age
164              
165             takes a 'delta-seconds' integer
166              
167             =cut
168              
169             # RFC 7234: ¤ 5.2.2.8. max-age
170             register http_cache_max_age => sub {
171             shift->_append_cache_control_directive_seconds('max-age' => @_);
172             return;
173             };
174              
175             =head2 http_cache_s_maxage
176              
177             see RFC 7234: ¤ 5.2.2.9. max-age
178              
179             takes a 'delta-seconds' integer
180              
181             =cut
182              
183             # RFC 7234: ¤ 5.2.2.9. s-maxage
184             register http_cache_s_maxage => sub {
185             shift->_append_cache_control_directive_seconds('s-maxage' => @_);
186             return;
187             };
188              
189             =head2 http_expire
190              
191             This Keywords set one of the two HTTP response headers that are related to
192             caching. It takes a HTTP Date formatted string that will tell any caching server
193             that the stored respource should be refreshed after the specified date/time
194              
195             See RFC 7234: ¤ 5.3
196              
197             =cut
198              
199             # RFC 7234: ¤ 5.3. Expires
200             register http_expire => sub {
201             $_[0]->log( warning =>
202             "http_expire: missing date" )
203             unless $_[1];
204             $_[0]->header('Expires' => $_[1]);
205             return;
206             };
207              
208             sub _append_cache_control {
209 0     0     my $dsl = shift;
210 0           my $directive = shift;
211 0           my $value = shift;
212            
213 0 0         $dsl->header('Cache-Control' =>
214             join ', ',
215             $dsl->header('Cache-Control'),
216             ( defined $value ? join '=', $directive, $value : $directive)
217             );
218 0           return $dsl->header('Cache-Control')
219             };
220              
221             sub _append_cache_control_directive {
222 0     0     my $dsl = shift;
223 0           my $directive = shift;
224            
225 0 0         $dsl->log( warning =>
226             "http_cache_control: '$directive' does not take any parameters"
227             ) if @_ ;
228            
229 0           return $dsl->_append_cache_control($directive, undef);
230             };
231              
232             sub _append_cache_control_directive_seconds {
233 0     0     my $dsl = shift;
234 0           my $directive = shift;
235 0   0       my $seconds = shift || 0;
236            
237 0 0         $dsl->log( warning =>
238             "http_cache_control: '$directive' does only take 'delta-seconds'"
239             ) if @_ ;
240            
241 0 0         $dsl->log( error =>
242             "http_cache_control: '$directive' requires number of seconds"
243             ) unless $seconds =~ /\d+/ ;
244            
245 0           my $value = $seconds;
246 0           return $dsl->_append_cache_control($directive, $value);
247             };
248              
249             sub _append_cache_control_directive_quoted {
250 0     0     my $dsl = shift;
251 0           my $directive = shift;
252 0 0         my @strings = ref $_[0] eq 'ARRAY' ? @$_[0] : @_;
253             # my @strings = @_;
254            
255 0 0         my $value = @strings ? '"' . join(' ', @strings) . '"' : undef;
256 0           return $dsl->_append_cache_control($directive, $value);
257             };
258              
259              
260              
261             on_plugin_import {
262             my $dsl = shift;
263             my $app = $dsl->app;
264             };
265              
266             register_plugin;
267              
268             =head1 AUTHOR
269              
270             Theo van Hoesel, C<< >>
271              
272             =head1 BUGS
273              
274             Please report any bugs or feature requests to
275             C, or through the web
276             interface at
277             L.
278             I will be notified, and then you'll automatically be notified of progress on
279             your bug as I make changes.
280              
281              
282              
283             =head1 SUPPORT
284              
285             You can find documentation for this module with the perldoc command.
286              
287             perldoc Dancer2::Plugin::HTTP::Caching
288              
289              
290             You can also look for information at:
291              
292             =over 4
293              
294             =item * RT: CPAN's request tracker (report bugs here)
295              
296             L
297              
298             =item * AnnoCPAN: Annotated CPAN documentation
299              
300             L
301              
302             =item * CPAN Ratings
303              
304             L
305              
306             =item * Search CPAN
307              
308             L
309              
310             =back
311              
312              
313             =head1 ACKNOWLEDGEMENTS
314              
315              
316             =head1 LICENSE AND COPYRIGHT
317              
318             Copyright 2015 Theo van Hoesel.
319              
320             This program is free software; you can redistribute it and/or modify it
321             under the terms of the the Artistic License (2.0). You may obtain a
322             copy of the full license at:
323              
324             L
325              
326             Any use, modification, and distribution of the Standard or Modified
327             Versions is governed by this Artistic License. By using, modifying or
328             distributing the Package, you accept this license. Do not use, modify,
329             or distribute the Package, if you do not accept this license.
330              
331             If your Modified Version has been derived from a Modified Version made
332             by someone other than you, you are nevertheless required to ensure that
333             your Modified Version complies with the requirements of this license.
334              
335             This license does not grant you the right to use any trademark, service
336             mark, tradename, or logo of the Copyright Holder.
337              
338             This license includes the non-exclusive, worldwide, free-of-charge
339             patent license to make, have made, use, offer to sell, sell, import and
340             otherwise transfer the Package with respect to any patent claims
341             licensable by the Copyright Holder that are necessarily infringed by the
342             Package. If you institute patent litigation (including a cross-claim or
343             counterclaim) against any party alleging that the Package constitutes
344             direct or contributory patent infringement, then this Artistic License
345             to you shall terminate on the date that such litigation is filed.
346              
347             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
348             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
349             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
350             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
351             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
352             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
353             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
354             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
355              
356             =cut
357              
358             1;