Snippets
Created by
Brian Medley
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | package Mojolicious::Plugin::RateLimit::Mojo::SQLite;
use Mojo::Base -base;
has migration => qq(
-- 1 up
CREATE TABLE mojolicious_plugin_ratelimit (
ip varchar(128),
hits integer,
route varchar(512),
inserted varchar(32),
updated varchar(32)
);
-- 1 down
DROP TABLE mojolicious_plugin_ratelimit;
);
has sql => sub {{
del => "DELETE FROM mojolicious_plugin_ratelimit",
sel_global => "SELECT hits, inserted, updated FROM mojolicious_plugin_ratelimit WHERE route IS NULL AND ip = ?",
sel_route => "SELECT hits, inserted, updated FROM mojolicious_plugin_ratelimit WHERE route IS ? AND ip = ?",
ins_global => "INSERT INTO mojolicious_plugin_ratelimit (ip, hits, route, inserted, updated) VALUES (?, 1, NULL, ?, ?)",
ins_route => "INSERT INTO mojolicious_plugin_ratelimit (ip, hits, route, inserted, updated) VALUES (?, 1, ?, ?, ?)",
upd_global => "UPDATE mojolicious_plugin_ratelimit SET hits = ?, updated = ? WHERE ip = ? AND route IS NULL",
upd_route => "UPDATE mojolicious_plugin_ratelimit SET hits = ?, updated = ? WHERE ip = ? AND route = ?",
}};
package Mojolicious::Plugin::RateLimit;
use Mojo::Base 'Mojolicious::Plugin';
use Mojo::Loader qw(load_class);
our $VERSION = '0.01';
sub register {
my ($self, $app, $conf) = @_;
my $method = $conf->{db_helper};
my $pkg = __PACKAGE__ . "::" . $conf->{db};
if (my $e = load_class($pkg)) {
die ref $e ? "Exception: $e" : "Not found: " . $pkg;
}
my $meta = $pkg->new;
my $sql = $conf->{sql} // $meta->sql;
$app->$method->migrations->from_string($meta->migration)->migrate;
$app->$method->db->query($sql->{del});
$app->hook(around_action => sub {
my ($next, $c, $action, $last) = @_;
my $ip = $c->tx->remote_address;
my $db = $app->$method->db;
my $global = $db->query($sql->{sel_global}, $ip)->hash;
if (!defined $global) {
my $now = time;
$db->query($sql->{ins_global}, $ip, $now, $now);
$db->query($sql->{ins_route}, $ip, $c->url_for->to_abs, $now, $now);
return $next->();
}
my $route = $db->query($sql->{sel_route}, $ip)->hash;
my $now = time;
++$global->{hits};
$db->query($sql->{upd_global}, $global->{hits}, $now, $ip)->hash;
$db->query($sql->{upd_route}, $global->{hits}, $now, $ip)->hash;
my $whence = $global->{updated} - $global->{inserted};
if ($whence >= 30) {
$app->$method->db->query($sql->{del});
return $next->();
}
if (30 <= $global->{hits}) {
return $c->respond_to(
json => {json => {error => "Too many requests"}, status => 429},
html => {text => "Too many requests\n", status => 429},
any => {text => "Too many requests\n", status => 429},
);
}
return $next->();
});
}
1;
__END__
=encoding utf8
=head1 NAME
Mojolicious::Plugin::RateLimit - Mojolicious Plugin
=head1 SYNOPSIS
# Mojolicious
$self->plugin('RateLimit');
# Mojolicious::Lite
plugin 'RateLimit';
=head1 DESCRIPTION
L<Mojolicious::Plugin::RateLimit> is a L<Mojolicious> plugin.
=head1 METHODS
L<Mojolicious::Plugin::RateLimit> inherits all methods from
L<Mojolicious::Plugin> and implements the following new ones.
=head2 register
$plugin->register(Mojolicious->new);
Register plugin in L<Mojolicious> application.
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
=cut
|
Comments (0)
You can clone a snippet to your computer for local editing. Learn more.