File Coverage

blib/lib/Plack/Middleware/Antibot.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


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