Snippets

Brian Medley A Mojo RateLimte Plugin

Created by Brian Medley

File RateLimit.pm Added

  • Ignore whitespace
  • Hide word diff
+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

File ratelimit.pl Added

  • Ignore whitespace
  • Hide word diff
+#!/opt/perl
+
+use Mojolicious::Lite;
+
+use Mojo::SQLite;
+
+helper db => sub {
+    state $sql = Mojo::SQLite->new('sqlite:test.db');
+};
+
+plugin 'RateLimit' => { db_helper => "db", db => "Mojo::SQLite" };
+
+get '/' => sub {
+    my $c = shift;
+    
+    $c->render(text => scalar(localtime) . "\n");
+};
+
+app->start;
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.