File Coverage

lib/Dancer/Plugin/TimeoutManager.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             package Dancer::Plugin::TimeoutManager;
2              
3 1     1   277244 use strict;
  1         1  
  1         30  
4 1     1   4 use warnings;
  1         1  
  1         33  
5             our $VERSION = '0.08'; # VERSION
6              
7 1     1   3 use Try::Tiny;
  1         5  
  1         55  
8 1     1   3 use Dancer ':syntax';
  1         1  
  1         4  
9 1     1   266 use Dancer::Exception ':all';
  1         2  
  1         112  
10 1     1   430 use Dancer::Plugin;
  1         1092  
  1         62  
11 1     1   5 use Data::Dumper;
  1         1  
  1         41  
12 1     1   6 use Carp 'croak';
  1         3  
  1         55  
13 1     1   136630 use List::MoreUtils qw( none);
  0            
  0            
14              
15             #get the timeout from headers
16             hook(before => sub {
17             var header_timeout => request->header('X-Dancer-Timeout');
18             });
19              
20             register 'timeout' => \&timeout;
21              
22             register_exception ('InvalidArgumentNumber',
23             message_pattern => "the number of arguments must 3 or 4, you've got %s",
24             );
25             register_exception ('InvalidMethod',
26             message_pattern => "method must be one in get, put, post, delete and %s is used as a method",
27             );
28              
29              
30             my @authorized_methods = ('get', 'post', 'put', 'delete');
31              
32             =method exception_message
33              
34             return the exception message
35             This method can be used to catch the exception if the code used already contained a try catch
36              
37             =cut
38              
39             sub exception_message{
40              
41             return 'Route Timeout Detected';
42             }
43              
44             =method timeout
45              
46             Method that manage the timeout on a dancer request
47              
48             =cut
49              
50             sub timeout {
51             my ($timeout,$method, $pattern, @rest);
52             if (scalar(@_) == 4){
53             ($timeout,$method, $pattern, @rest) = @_;
54             }
55             elsif(scalar(@_) == 3){
56             ($method, $pattern, @rest) = @_;
57             }
58             else{
59             raise InvalidMethod => scalar(@_);
60             }
61             my $code;
62             for my $e (@rest) { $code = $e if (ref($e) eq 'CODE') }
63             my $request;
64              
65             #if method is not valid an exception is done
66             if ( none { $_ eq lc($method) } @authorized_methods ){
67             raise InvalidMethod => $method;
68             }
69            
70             my $exception_message = exception_message();
71             my $timeout_route = sub {
72             my $response;
73              
74             #if timeout is not defined but a value is set in the headers for timeout
75             $timeout = vars->{header_timeout} if (!defined $timeout && defined vars->{header_timeout});
76              
77             # if timeout is not defined or equal 0 the timeout manager is not used
78             my $timeout_exception;
79             if (!$timeout){
80             $response = $code->();
81             }
82             else{
83             try {
84             local $SIG{ALRM} = sub { croak ($exception_message); };
85             alarm($timeout);
86              
87             $response = $code->();
88             alarm(0);
89             }
90             catch{
91             $timeout_exception = $_;
92             };
93             alarm(0);
94             }
95             # Timeout detected
96             if ($timeout_exception && $timeout_exception =~ /$exception_message/){
97             my $response_with_timeout = Dancer::Response->new(
98             status => 408,
99             content => "Request Timeout : more than $timeout seconds elapsed."
100             );
101             return $response_with_timeout;
102             }
103             # Preserve exceptions caught during route call
104             croak $@ if $@;
105              
106             # else everything is allright
107             return $response;
108             };
109              
110              
111             my @compiled_rest;
112             for my $e (@rest) {
113             if (ref($e) eq 'CODE') {
114             push @compiled_rest, $timeout_route;
115             }
116             else {
117             push @compiled_rest, $e;
118             }
119             }
120              
121             # declare the route in Dancer's registry
122             any [$method] => $pattern, @compiled_rest;
123             }
124              
125             register_plugin;
126              
127              
128             1;
129             __END__