Skip to content

Commit

Permalink
Adding Throttle Middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
javad-zobeidi committed Mar 26, 2024
1 parent f93d65d commit 14b199a
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 4 deletions.
3 changes: 2 additions & 1 deletion lib/src/exception/http_exception.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:vania/vania.dart';
import '../http/response/response.dart';
import 'base_http_exception.dart';

class HttpException extends BaseHttpException {
HttpException(
Expand Down
1 change: 0 additions & 1 deletion lib/src/exception/internal_server_error.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:io';

import 'base_http_exception.dart';

class InternalServerError extends BaseHttpException {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/exception/not_found_exception.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:io';

import 'package:vania/vania.dart';
import '../http/response/response.dart';
import 'base_http_exception.dart';

class NotFoundException extends BaseHttpException {
NotFoundException({
Expand Down
10 changes: 10 additions & 0 deletions lib/src/exception/throttle_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import '../http/response/response.dart';
import 'base_http_exception.dart';

class ThrottleException extends BaseHttpException {
ThrottleException(
{required super.message,
required super.code,
super.responseType = ResponseType.json,
super.errorCode = 'Rate limiting'});
}
39 changes: 39 additions & 0 deletions lib/src/route/middleware/throttle.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'dart:io';

import 'package:vania/vania.dart';

import '../../exception/throttle_exception.dart';
import '../throttle_requests.dart';

class Throttle extends Middleware {
final int maxAttempts;
final Duration duration;

Throttle({
this.maxAttempts = 6,
this.duration = const Duration(seconds: 60),
}) {
throttle = ThrottleRequests(maxAttempts: maxAttempts, duration: duration);
}

late ThrottleRequests throttle;

@override
Future handle(Request req) async {
final clientIp = req.ip;
if (clientIp == null) {
req.response.statusCode = HttpStatus.internalServerError;
req.response.write('Error determining client IP');
await req.response.close();
return;
}

if (!throttle.request(clientIp)) {
throw ThrottleException(
message: 'Too Many Requests.',
code: HttpStatus.tooManyRequests,
);
}
next?.handle(req);
}
}
33 changes: 33 additions & 0 deletions lib/src/route/throttle_requests.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class ThrottleRequests {
final int maxAttempts;
final Duration duration;
final Map<String, _RateLimit> _limits = {};

ThrottleRequests({required this.maxAttempts, required this.duration});

bool request(String ip) {
final currentTime = DateTime.now();

_limits.putIfAbsent(ip, () => _RateLimit(0, currentTime));
final limit = _limits[ip]!;

if (currentTime.difference(limit.windowStart).compareTo(duration) >= 0) {
limit.count = 0;
limit.windowStart = currentTime;
}

if (limit.count < maxAttempts) {
limit.count++;
return true;
} else {
return false;
}
}
}

class _RateLimit {
int count;
DateTime windowStart;

_RateLimit(this.count, this.windowStart);
}
1 change: 1 addition & 0 deletions lib/vania.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export 'src/websocket/websocket_event.dart';

export 'src/route/router.dart';
export 'src/route/route.dart';
export 'src/route/middleware/throttle.dart';

export 'src/cache/cache_driver.dart';
export 'src/cache/cache.dart';
Expand Down

0 comments on commit 14b199a

Please # to comment.