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   449 use strict;
  1         1  
  1         24  
4 1     1   5 use warnings;
  1         1  
  1         21  
5 1     1   387 use parent 'Plack::Middleware';
  1         286  
  1         5  
6 1     1   13285 use Plack::Util::Accessor qw(timeout response soft_timeout on_soft_timeout);
  1         3  
  1         4  
7 1     1   502 use Plack::Request;
  1         64865  
  1         30  
8 1     1   399 use Plack::Response;
  1         954  
  1         25  
9 1     1   376 use Scope::Guard ();
  1         351  
  1         23  
10 1     1   1244 use Time::HiRes qw(alarm time);
  1         1309  
  1         4  
11 1     1   157 use Carp qw(croak);
  1         2  
  1         56  
12 1     1   424 use HTTP::Status qw(HTTP_GATEWAY_TIMEOUT);
  1         3914  
  1         402  
13              
14             our $VERSION = '0.10';
15              
16             sub prepare_app {
17 4     4 1 6640 my $self = shift;
18              
19 4 50       18 $self->timeout(120) unless $self->timeout;
20              
21 4         67 for my $param (qw(response on_soft_timeout)) {
22 8 100       53 next unless defined $self->$param;
23 1 50       8 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 20117 my ( $self, $env ) = @_;
34              
35 4         14 my $alarm_msg = 'Plack::Middleware::Timeout';
36 4     2   102 local $SIG{ALRM} = sub { die $alarm_msg };
  2         8000485  
37              
38 4         16 my $time_started = 0;
39 4         11 local $@;
40             eval {
41              
42 4         20 $time_started = time();
43 4         28 alarm($self->timeout);
44              
45             my $guard = Scope::Guard->new(sub {
46 4     4   273 alarm 0;
47 4         103 });
48              
49 4         72 my $soft_timeout_guard;
50              
51 4 100       17 if ( my $soft_timeout = $self->soft_timeout ) {
52             $soft_timeout_guard = Scope::Guard->new(
53             sub {
54 2 100   2   4000532 if ( time() - $time_started > $soft_timeout ) {
55 1   33     14 my $on_soft_timeout =
56             $self->on_soft_timeout || $default_on_soft_timeout;
57              
58 1         41 my $request = Plack::Request->new($env);
59              
60 1         26 $on_soft_timeout->(
61             $request->uri,
62             $soft_timeout,
63             );
64             }
65             }
66 2         28 );
67             }
68              
69 4         54 return $self->app->($env);
70              
71 4 100       10 } or do {
72 2         53 my $request = Plack::Request->new($env);
73              
74 2         71 my $response = Plack::Response->new(HTTP_GATEWAY_TIMEOUT);
75 2 100       90 if ( my $build_response_coderef = $self->response ) {
76 1         14 $build_response_coderef->($response);
77             }
78             else {
79             # warn by default, so there's a trace of the timeout left somewhere
80 1         21 warn sprintf
81             "Terminated request for uri '%s' due to timeout (%ds)",
82             $request->uri,
83             $self->timeout;
84             }
85              
86 2         648 return $response->finalize;
87             };
88             }
89              
90             1;
91              
92             __END__