Skip to content

Commit 167af20

Browse files
committed
feat(middlewares): implement rate limiter middleware
- Add rateLimiter middleware function to enforce rate limiting on routes - Include ipKeyExtractor for IP-based rate limiting - Implement _getIpAddress to extract client's IP address from request - Add RateLimitService for tracking and limiting requests
1 parent 2f82e09 commit 167af20

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
import 'package:flutter_news_app_api_server_full_source_code/src/services/rate_limit_service.dart';
3+
4+
/// Extracts the client's IP address from the request.
5+
///
6+
/// It prioritizes the 'X-Forwarded-For' header, which is standard for
7+
/// identifying the originating IP of a client connecting through a proxy
8+
/// or load balancer. If that header is not present, it falls back to the
9+
/// direct connection's IP address.
10+
String? _getIpAddress(RequestContext context) {
11+
final headers = context.request.headers;
12+
// The 'X-Forwarded-For' header can contain a comma-separated list of IPs.
13+
// The first one is typically the original client IP.
14+
final xff = headers['X-Forwarded-For']?.split(',').first.trim();
15+
if (xff != null && xff.isNotEmpty) {
16+
return xff;
17+
}
18+
// Fallback to the direct connection IP if XFF is not available.
19+
return context.request.connectionInfo?.remoteAddress.address;
20+
}
21+
22+
/// Middleware to enforce rate limiting on a route.
23+
///
24+
/// This middleware uses the [RateLimitService] to track and limit the number
25+
/// of requests from a unique source (identified by a key) within a specific
26+
/// time window.
27+
///
28+
/// - [limit]: The maximum number of requests allowed.
29+
/// - [window]: The time duration in which the requests are counted.
30+
/// - [keyExtractor]: A function that extracts a unique key from the request
31+
/// context. This could be an IP address, an email from the body, etc.
32+
Middleware rateLimiter({
33+
required int limit,
34+
required Duration window,
35+
required Future<String?> Function(RequestContext) keyExtractor,
36+
}) {
37+
return (handler) {
38+
return (context) async {
39+
final rateLimitService = context.read<RateLimitService>();
40+
final key = await keyExtractor(context);
41+
42+
// If a key cannot be extracted, we bypass the rate limiter.
43+
// This is a safeguard; for IP-based limiting, a key should always exist.
44+
if (key == null || key.isEmpty) {
45+
return handler(context);
46+
}
47+
48+
// The checkRequest method will throw a ForbiddenException if the
49+
// limit is exceeded. This will be caught by the global error handler.
50+
await rateLimitService.checkRequest(
51+
key: key,
52+
limit: limit,
53+
window: window,
54+
);
55+
56+
// If the check passes, proceed to the next handler.
57+
return handler(context);
58+
};
59+
};
60+
}
61+
62+
/// A specific implementation of the [keyExtractor] for IP-based rate limiting.
63+
Future<String?> ipKeyExtractor(RequestContext context) async {
64+
return _getIpAddress(context);
65+
}

0 commit comments

Comments
 (0)