Plack-Middleware-Greylist

 view release on metacpan or  search on metacpan

t/05-callback.t  view on Meta::CPAN

use v5.20;
use warnings;

use Test2::V0;
use Test2::Tools::Compare;

use HTTP::Request::Common;
use HTTP::Status qw/ :constants status_message /;
use Path::Tiny;
use Plack::Builder;
use Plack::Response;
use Plack::Test;
use Plack::Middleware::ReverseProxy;

use experimental qw/ signatures /;

my $file = Path::Tiny->tempfile;

my %greylist = ( "172.16.0.0/24" => [ 5, "netblock" ], );

my @logs;
my @calls;

my $handler = builder {

    # Capture log messages
    enable sub($app) {
        sub($env) {
            $env->{'psgix.logger'} = sub {
                push @logs, $_[0];
            };
            return $app->($env);
        };
    };

    # Trust the "X-Forwarded-For" header
    enable "ReverseProxy";

    enable "Greylist",
      default_rate => 5,
      cache_config => {
        init_file      => 1,
        unlink_on_exit => 1,
        expire_time    => 30,
        share_file     => $file,
      },
      greylist => \%greylist,
      callback => sub {
        return 0 if $_[0]->{env}{REQUEST_URI} eq "/?ok";
        push @calls, $_[0];
        return 1;
      };

    sub($env) {
        my $res = Plack::Response->new( HTTP_OK, [ 'Content-Type' => 'text/plain' ], [ status_message(HTTP_OK) ] );
        return $res->finalize;
    }
};

subtest "rate limiting" => sub {

    @logs = ();

    test_psgi
      app    => $handler,
      client => sub($cb) {

        for my $suff ( 1 .. 5 ) {
            my $req = HEAD "/", "X-Forwarded-For" => "172.16.0.${suff}";
            my $res = $cb->($req);
            is $res->code, HTTP_OK, "request ok";
        }

        {
            my $req = HEAD "/?ok", "X-Forwarded-For" => "172.16.0.10";
            my $res = $cb->($req);
            is $res->code, HTTP_OK, "request ok (callback override)";
        }

        my $req = HEAD "/", "X-Forwarded-For" => "172.16.0.10";
        my $res = $cb->($req);
        is $res->code, HTTP_TOO_MANY_REQUESTS, "too many requests";

        is $res->header('Retry-After'), 31, "Retry-After set from expire_time";

        is \@logs, [], "nothing logged";

        is \@calls, [
            hash(
                sub {
                    field block   => "172.16.0.0/24";
                    field ip      => "172.16.0.10";
                    field hits    => 7;
                    field rate    => 5;
                    field message => "Rate limiting 172.16.0.10 after 7/5 for 172.16.0.0/24";
                    field env => hash(
                        sub {
                            field REMOTE_ADDR => "172.16.0.10";
                            etc;
                        }
                    );

                }
            )
          ],
          "callbacks";

      };

};

done_testing;



( run in 1.605 second using v1.01-cache-2.11-cpan-39bf76dae61 )