File Coverage

blib/lib/Plack/Middleware/ServerStatus/Availability.pm
Criterion Covered Total %
statement 75 79 94.9
branch 25 34 73.5
condition 12 18 66.6
subroutine 16 16 100.0
pod 2 5 40.0
total 130 152 85.5


line stmt bran cond sub pod time code
1             package Plack::Middleware::ServerStatus::Availability;
2 3     3   76536 use strict;
  3         7  
  3         112  
3 3     3   14 use warnings;
  3         4  
  3         80  
4              
5 3     3   431 use parent qw(Plack::Middleware);
  3         331  
  3         12  
6 3     3   10216 use Plack::Util::Accessor qw(path file allow);
  3         4  
  3         13  
7 3     3   1912 use Plack::Request;
  3         164332  
  3         116  
8 3     3   1764 use Net::CIDR::Lite;
  3         11402  
  3         1853  
9              
10             our $VERSION = "0.01";
11              
12             sub prepare_app {
13 2     2 1 562 my ($self) = @_;
14 2 50       10 unless ($self->path->{status}) {
15 0         0 warn sprintf "[%s] 'path.status' is not provided.", __PACKAGE__;
16             }
17 2 50       173 unless ($self->path->{control}) {
18 0         0 warn sprintf "[%s] 'path.control' is not provided.", __PACKAGE__;
19             }
20 2 50       21 unless ($self->file) {
21 0         0 warn sprintf "[%s] 'file' is not provided.", __PACKAGE__;
22             }
23 2 50       24 unless ($self->allow) {
24 0         0 warn sprintf "[%s] 'allow' is not provided.", __PACKAGE__;
25             }
26              
27 2 50       22 if ($self->allow) {
28 2 100       15 my @allow = ref $self->allow ? @{$self->allow} : ($self->allow);
  1         10  
29 2         23 my $ip = { v4 => [], v6 => [] };
30 2 50       7 push @{$ip->{$_ =~ /:/ ? 'v6' : 'v4'}}, $_ for @allow;
  1         7  
31 2         5 $self->{__cidr} = {};
32 2         6 for my $v (qw(v4 v6)) {
33 4 100       8 if (@{$ip->{$v}}) {
  4         20  
34 1         9 my $cidr = Net::CIDR::Lite->new();
35 1         10 $cidr->add_any($_) for @{$ip->{$v}};
  1         6  
36 1         185 $self->{__cidr}->{$v} = $cidr;
37             }
38             }
39             }
40             };
41              
42             sub call {
43 13     13 1 72168 my ($self, $env) = @_;
44              
45 13         114 my $req = Plack::Request->new($env);
46 13         132 my $addr = $env->{REMOTE_ADDR};
47 13 50 66     60 if ($self->path->{status} and $self->path->{status} eq $req->path
      66        
      66        
48             and $req->method eq 'GET' and $self->file) {
49 5 100       251 return $self->respond(403, 'Forbidden') unless $self->allowed($addr);
50 4 100       246 if ($self->status->is_available) {
51 2         114 return $self->respond(200, 'OK');
52             } else {
53 2         109 return $self->respond(503, 'Server is up but is under maintenance');
54             }
55             }
56 8 50 66     233 if ($self->path->{control} and $self->path->{control} eq $req->path
      66        
      66        
57             and $req->method eq 'POST' and $self->file) {
58 7 100       245 return $self->respond(403, 'Forbidden') unless $self->allowed($addr);
59 4         196 my $action = $req->param('action');
60 4 100       858 if ($action eq 'up') {
    100          
61 2         7 $self->status->up;
62 2         20 return $self->respond(200, 'Done');
63             } elsif ($action eq 'down') {
64 1         4 $self->status->down;
65 1         200 return $self->respond(200, 'Done');
66             } else {
67 1         4 return $self->respond(400, 'Bad action');
68             }
69             }
70              
71 1         31 return $self->app->($env);
72             }
73              
74             sub allowed {
75 12     12 0 24 my ($self, $addr) = @_;
76 12 50       50 my $v = ( $addr =~ /:/ ? 'v6' : 'v4' );
77 12 100       66 return unless $self->{__cidr}->{$v};
78 8         41 return $self->{__cidr}->{$v}->find($addr);
79             }
80              
81             sub status {
82 7     7 0 21 my $file = $_[0]->file;
83 7         101 return Plack::Middleware::ServerStatus::Availability::Status->new($file);
84             }
85              
86             sub respond {
87 12     12 0 29 my ($self, $code, $reason) = @_;
88 12         198 return [ $code, [ 'Content-Type' => 'text/plain' ], [ "$code $reason" ] ];
89             }
90              
91             package Plack::Middleware::ServerStatus::Availability::Status;
92 3     3   1694 use Path::Class;
  3         63816  
  3         629  
93              
94             sub new {
95 7     7   11 my ($class, $file) = @_;
96 7         29 return bless { file => file($file) }, $class;
97             }
98              
99             sub is_available {
100 4     4   651 return -f $_[0]->{file};
101             }
102              
103             sub up {
104 2     2   238 my $file = $_[0]->{file};
105 2         10 $file->dir->mkpath;
106 2         169 my $fh = $file->openw;
107 2         524 printf $fh "%d\n", time;
108 2         236 close $fh;
109             }
110              
111             sub down {
112 1     1   115 $_[0]->{file}->remove;
113             }
114              
115             package Plack::Middleware::ServerStatus::Availability;
116              
117             1;
118             __END__