File Coverage

blib/lib/Plack/Middleware/Antibot.pm
Criterion Covered Total %
statement 57 57 100.0
branch 13 14 92.8
condition 2 5 40.0
subroutine 9 9 100.0
pod 2 2 100.0
total 83 87 95.4


line stmt bran cond sub pod time code
1             package Plack::Middleware::Antibot;
2              
3 2     2   1786 use strict;
  2         3  
  2         67  
4 2     2   8 use warnings;
  2         2  
  2         61  
5              
6 2     2   478 use parent 'Plack::Middleware';
  2         284  
  2         15  
7              
8             our $VERSION = "0.02";
9              
10 2     2   10876 use List::Util qw(sum reduce);
  2         4  
  2         196  
11 2     2   10 use Plack::Util ();
  2         2  
  2         33  
12 2     2   8 use Plack::Util::Accessor qw(filters fall_through max_score);
  2         406  
  2         15  
13              
14             sub prepare_app {
15 6     6 1 27384 my $self = shift;
16              
17 6   50     109 $self->{max_score} ||= 0.8;
18              
19 6         17 my $filters_names = $self->filters;
20              
21 6         28 my @filters;
22 6         10 foreach my $filter (@$filters_names) {
23 7         8 my @args;
24 7 100       16 if (ref $filter eq 'ARRAY') {
25 3         4 my $ref = $filter;
26 3         5 $filter = shift @$ref;
27 3         8 @args = @$ref;
28             }
29              
30 7         14 my $filter_class = __PACKAGE__ . '::' . $filter;
31              
32 7         15 Plack::Util::load_class($filter_class);
33              
34 7         93 push @filters, $filter_class->new(@args);
35             }
36              
37 6         20 $self->filters(\@filters);
38              
39 6         43 return $self;
40             }
41              
42             sub call {
43 8     8 1 25207 my $self = shift;
44 8         12 my ($env) = @_;
45              
46 8         9 my @scores;
47 8         9 my $current_score = 0;
48 8         9 foreach my $filter (@{$self->filters}) {
  8         21  
49 9         68 my $res = $filter->execute($env);
50 9 50 33     23 return $res if $res && ref $res eq 'ARRAY';
51              
52 9         32 my $name = (split /::/, ref $filter)[-1];
53 9         21 my $key = 'plack.antibot.' . lc($name) . '.detected';
54              
55 9 100       23 if ($env->{$key}) {
56 6         26 push @scores, $filter->score;
57              
58 6 100       14 if (@scores > 1) {
59 1         20 my $p = sum @scores;
60 1     1   17 my $q = reduce { $a * $b } @scores;
  1         3  
61              
62 1         4 $current_score = $p - $q;
63             }
64             else {
65 5         13 $current_score = $filter->score;
66             }
67             }
68              
69 9 100       25 last if $current_score >= $self->max_score;
70             }
71              
72 8         61 $env->{'plack.antibot.score'} = $current_score;
73              
74 8 100       17 if ($current_score >= $self->max_score) {
75 4         26 $env->{'plack.antibot.detected'} = 1;
76              
77 4 100       11 return [400, [], ['Bad request']] unless $self->fall_through;
78             }
79              
80 6         44 return $self->app->($env);
81             }
82              
83             1;
84             __END__