@@ -36,7 +36,7 @@ public class RateLimitingFilter implements Filter {
3636 * Constructs a new RateLimitingFilter.
3737 *
3838 * @param requestsPerMinute the number of requests allowed per minute for each IP
39- * @param burstCapacity the maximum number of requests that can be handled in a burst
39+ * @param burstCapacity the maximum number of requests that can be handled in a burst
4040 * @throws IllegalArgumentException if requestsPerMinute or burstCapacity is not positive
4141 */
4242 public RateLimitingFilter (long requestsPerMinute , long burstCapacity ) {
@@ -81,7 +81,7 @@ public RateLimitingFilter() {
8181 }
8282
8383 /**
84- * Applies the rate limiting logic to the incoming request.
84+ * Applies the rate- limiting logic to the incoming request.
8585 * If the rate limit is exceeded, a 429 Too Many Requests response is sent.
8686 *
8787 * @param req the HTTP request
@@ -91,7 +91,10 @@ public RateLimitingFilter() {
9191 */
9292 @ Override
9393 public void doFilter (HttpRequest req , HttpResponse res , FilterChain chain ) throws IOException {
94- if (!enabled ) {chain .doFilter (req , res );return ;}
94+ if (!enabled ) {
95+ chain .doFilter (req , res );
96+ return ;
97+ }
9598
9699 String clientIp = getClientIp (req );
97100
@@ -101,7 +104,7 @@ public void doFilter(HttpRequest req, HttpResponse res, FilterChain chain) throw
101104 chain .doFilter (req , res );
102105 } else {
103106 logRateLimitExceeded (clientIp , req .method (), req .path ());
104- sendTooManyRequests (res , clientIp );
107+ sendTooManyRequests (res );
105108 }
106109 }
107110
@@ -135,15 +138,52 @@ private void logRateLimitExceeded(String ip, String method, String path) {
135138 ));
136139 }
137140
138- private void sendTooManyRequests (HttpResponse res , String ip ) {
139- byte [] body = ("429 Too Many Requests: Rate limit exceeded for IP " + ip + "\n " )
140- .getBytes (StandardCharsets .UTF_8 );
141+ private void sendTooManyRequests (HttpResponse res ) {
142+ String html = """
143+ <!DOCTYPE html>
144+ <html>
145+ <head>
146+ <meta charset="UTF-8">
147+ <title>429 Too Many Requests</title>
148+ <style>
149+ body {
150+ font-family: Arial, sans-serif;
151+ max-width: 600px;
152+ margin: 100px auto;
153+ text-align: center;
154+ }
155+ h1 { color: #e74c3c; }
156+ p { color: #666; }
157+ </style>
158+ </head>
159+ <body>
160+ <h1>429 - Too Many Requests</h1>
161+ <p>You've exceeded the rate limit. Please wait a moment and try again.</p>
162+ <p>You will be able to retry in <span id="countdown">60</span> seconds.</p>
163+ <script>
164+ let seconds = 60;
165+ const el = document.getElementById('countdown');
166+ const timer = setInterval(() => {
167+ seconds--;
168+ el.textContent = seconds;
169+ if (seconds <= 0) {
170+ clearInterval(timer);
171+ window.location.href = '/';
172+ }
173+ }, 1000);
174+ </script>
175+ </body>
176+ </html>
177+ """ ;
178+
179+ byte [] body = html .getBytes (StandardCharsets .UTF_8 );
141180
142181 res .setStatusCode (429 );
143182 res .setStatusText ("Too Many Requests" );
144- res .setHeader ("Content-Type" , "text/plain ; charset=utf-8" );
183+ res .setHeader ("Content-Type" , "text/html ; charset=utf-8" );
145184 res .setHeader ("Content-Length" , String .valueOf (body .length ));
146185 res .setHeader ("Retry-After" , "60" );
186+ res .setHeader ("Cache-Control" , "no-store" );
147187 res .setBody (body );
148188 }
149189
0 commit comments