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   223654 use strict;
  1         2  
  1         28  
4 1     1   4 use warnings;
  1         1  
  1         31  
5             our $VERSION = '0.09'; # VERSION
6              
7 1     1   4 use Try::Tiny;
  1         4  
  1         79  
8 1     1   7 use Dancer ':syntax';
  1         1  
  1         7  
9 1     1   277 use Dancer::Exception ':all';
  1         1  
  1         121  
10 1     1   492 use Dancer::Plugin;
  1         1118  
  1         78  
11 1     1   6 use Data::Dumper;
  1         1  
  1         39  
12 1     1   4 use Carp 'croak';
  1         2  
  1         31  
13 1     1   124770 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             my $request_timeout = 0;
76             $request_timeout = $timeout if (defined $timeout);
77             $request_timeout = vars->{header_timeout} if (!defined $timeout && defined vars->{header_timeout});
78              
79             # if timeout is not defined or equal 0 the timeout manager is not used
80             my $timeout_exception;
81             if (!$request_timeout){
82             $response = $code->();
83             }
84             else{
85             try {
86             local $SIG{ALRM} = sub { croak ($exception_message); };
87             alarm($request_timeout);
88              
89             $response = $code->();
90             alarm(0);
91             }
92             catch{
93             $timeout_exception = $_;
94             };
95             alarm(0);
96             }
97             # Timeout detected
98             if ($timeout_exception && $timeout_exception =~ /$exception_message/){
99             my $response_with_timeout = Dancer::Response->new(
100             status => 408,
101             content => "Request Timeout : more than $request_timeout seconds elapsed."
102             );
103             return $response_with_timeout;
104             }
105             # Preserve exceptions caught during route call
106             croak $@ if $@;
107              
108             # else everything is allright
109             return $response;
110             };
111              
112              
113             my @compiled_rest;
114             for my $e (@rest) {
115             if (ref($e) eq 'CODE') {
116             push @compiled_rest, $timeout_route;
117             }
118             else {
119             push @compiled_rest, $e;
120             }
121             }
122              
123             # declare the route in Dancer's registry
124             any [$method] => $pattern, @compiled_rest;
125             }
126              
127             register_plugin;
128              
129              
130             1;
131             __END__