File Coverage

blib/lib/Plack/Middleware/Timeout.pm
Criterion Covered Total %
statement 60 60 100.0
branch 12 14 85.7
condition 1 3 33.3
subroutine 15 15 100.0
pod 2 2 100.0
total 90 94 95.7


line stmt bran cond sub pod time code
1             package Plack::Middleware::Timeout;
2              
3 1     1   314 use strict;
  1         3  
  1         37  
4 1     1   7 use warnings;
  1         3  
  1         42  
5 1     1   212 use parent 'Plack::Middleware';
  1         244  
  1         4  
6 1     1   10150 use Plack::Util::Accessor qw(timeout response soft_timeout on_soft_timeout);
  1         7  
  1         4  
7 1     1   308 use Plack::Request;
  1         48207  
  1         36  
8 1     1   269 use Plack::Response;
  1         778  
  1         28  
9 1     1   234 use Scope::Guard ();
  1         299  
  1         20  
10 1     1   308 use Time::HiRes qw(alarm time);
  1         925  
  1         4  
11 1     1   138 use Carp qw(croak);
  1         2  
  1         42  
12 1     1   276 use HTTP::Status qw(HTTP_GATEWAY_TIMEOUT);
  1         2987  
  1         375  
13              
14             our $VERSION = '0.09';
15              
16             sub prepare_app {
17 4     4 1 5931 my $self = shift;
18              
19 4 50       18 $self->timeout(120) unless $self->timeout;
20              
21 4         76 for my $param (qw(response on_soft_timeout)) {
22 8 100       49 next unless defined $self->$param;
23 1 50       6 croak "parameter $param isn't a CODE reference!"
24             unless ref( $self->$param ) eq 'CODE';
25             }
26             }
27              
28             my $default_on_soft_timeout = sub {
29             warn sprintf "Soft timeout reached for uri '%s' (soft timeout: %ds) request took %ds", @_;
30             };
31              
32             sub call {
33 4     4 1 18700 my ( $self, $env ) = @_;
34              
35 4         12 my $alarm_msg = 'Plack::Middleware::Timeout';
36 4     2   66 local $SIG{ALRM} = sub { die $alarm_msg };
  2         8000306  
37              
38 4         15 my $time_started = 0;
39 4         7 local $@;
40             eval {
41              
42 4         24 $time_started = time();
43 4         17 alarm($self->timeout);
44              
45             my $guard = Scope::Guard->new(sub {
46 4     4   246 alarm 0;
47 4         75 });
48              
49 4         66 my $soft_timeout_guard;
50              
51 4 100       16 if ( my $soft_timeout = $self->soft_timeout ) {
52             $soft_timeout_guard = Scope::Guard->new(
53             sub {
54 2 100   2   4000467 if ( time() - $time_started > $soft_timeout ) {
55 1   33     10 my $on_soft_timeout =
56             $self->on_soft_timeout || $default_on_soft_timeout;
57              
58 1         26 my $request = Plack::Request->new($env);
59              
60 1         26 $on_soft_timeout->(
61             $request->uri,
62             $soft_timeout,
63             );
64             }
65             }
66 2         32 );
67             }
68              
69 4         57 return $self->app->($env);
70              
71 4 100       11 } or do {
72 2         39 my $request = Plack::Request->new($env);
73              
74 2         57 my $response = Plack::Response->new(HTTP_GATEWAY_TIMEOUT);
75 2 100       84 if ( my $build_response_coderef = $self->response ) {
76 1         13 $build_response_coderef->($response);
77             }
78             else {
79             # warn by default, so there's a trace of the timeout left somewhere
80 1         18 warn sprintf
81             "Terminated request for uri '%s' due to timeout (%ds)",
82             $request->uri,
83             $self->timeout;
84             }
85              
86 2         568 return $response->finalize;
87             };
88             }
89              
90             1;
91              
92             __END__