You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2023/05/09 19:42:33 UTC

[tomcat] 02/03: Clean-up - formatting. No functional change.

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 7ba678c566dc82e4f169e5bc25313b310140df8f
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue May 9 20:38:49 2023 +0100

    Clean-up - formatting. No functional change.
---
 .../apache/catalina/filters/RateLimitFilter.java   | 91 ++++++++++------------
 .../apache/catalina/util/TimeBucketCounter.java    | 60 ++++++--------
 .../catalina/filters/TestRateLimitFilter.java      | 14 ++--
 3 files changed, 73 insertions(+), 92 deletions(-)

diff --git a/java/org/apache/catalina/filters/RateLimitFilter.java b/java/org/apache/catalina/filters/RateLimitFilter.java
index 94e615452a..097485aef3 100644
--- a/java/org/apache/catalina/filters/RateLimitFilter.java
+++ b/java/org/apache/catalina/filters/RateLimitFilter.java
@@ -33,45 +33,41 @@ import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.res.StringManager;
 
 /**
- * <p>Servlet filter that can help mitigate Denial of Service
- * (DoS) and Brute Force attacks by limiting the number of a requests that are
- * allowed from a single IP address within a time window (also referred
- * to as a time bucket), e.g. 300 Requests per 60 seconds.</p>
- *
- * <p>The filter works by incrementing a counter in a time bucket for each IP
- * address, and if the counter exceeds the allowed limit then further requests
- * from that IP are dropped with a &quot;429 Too many requests&quot; response
- * until the bucket time ends and a new bucket starts.</p>
- *
- * <p>The filter is optimized for efficiency and low overhead, so it converts
- * some configured values to more efficient values. For example, a configuration
- * of a 60 seconds time bucket is converted to 65.536 seconds. That allows
- * for very fast bucket calculation using bit shift arithmetic. In order to remain
- * true to the user intent, the configured number of requests is then multiplied
- * by the same ratio, so a configuration of 100 Requests per 60 seconds, has the
- * real values of 109 Requests per 65 seconds.</p>
- *
- * <p>It is common to set up different restrictions for different URIs.
- * For example, a login page or authentication script is typically expected
- * to get far less requests than the rest of the application, so you can add
- * a filter definition that would allow only 5 requests per 15 seconds and map
- * those URIs to it.</p>
- *
- * <p>You can set <code>enforce</code> to <code>false</code>
- * to disable the termination of requests that exceed the allowed limit. Then
- * your application code can inspect the Request Attribute
- * <code>org.apache.catalina.filters.RateLimitFilter.Count</code> and decide
- * how to handle the request based on other information that it has, e.g. allow
- * more requests to certain users based on roles, etc.</p>
- *
- * <p><strong>WARNING:</strong> if Tomcat is behind a reverse proxy then you must
- * make sure that the Rate Limit Filter sees the client IP address, so if for
- * example you are using the <a href="#Remote_IP_Filter">Remote IP Filter</a>,
- * then the filter mapping for the Rate Limit Filter must come <em>after</em>
- * the mapping of the Remote IP Filter to ensure that each request has its IP
- * address resolved before the Rate Limit Filter is applied. Failure to do so
- * will count requests from different IPs in the same bucket and will result in
- * a self inflicted DoS attack.</p>
+ * <p>
+ * Servlet filter that can help mitigate Denial of Service (DoS) and Brute Force attacks by limiting the number of a
+ * requests that are allowed from a single IP address within a time window (also referred to as a time bucket), e.g. 300
+ * Requests per 60 seconds.
+ * </p>
+ * <p>
+ * The filter works by incrementing a counter in a time bucket for each IP address, and if the counter exceeds the
+ * allowed limit then further requests from that IP are dropped with a &quot;429 Too many requests&quot; response until
+ * the bucket time ends and a new bucket starts.
+ * </p>
+ * <p>
+ * The filter is optimized for efficiency and low overhead, so it converts some configured values to more efficient
+ * values. For example, a configuration of a 60 seconds time bucket is converted to 65.536 seconds. That allows for very
+ * fast bucket calculation using bit shift arithmetic. In order to remain true to the user intent, the configured number
+ * of requests is then multiplied by the same ratio, so a configuration of 100 Requests per 60 seconds, has the real
+ * values of 109 Requests per 65 seconds.
+ * </p>
+ * <p>
+ * It is common to set up different restrictions for different URIs. For example, a login page or authentication script
+ * is typically expected to get far less requests than the rest of the application, so you can add a filter definition
+ * that would allow only 5 requests per 15 seconds and map those URIs to it.
+ * </p>
+ * <p>
+ * You can set <code>enforce</code> to <code>false</code> to disable the termination of requests that exceed the allowed
+ * limit. Then your application code can inspect the Request Attribute
+ * <code>org.apache.catalina.filters.RateLimitFilter.Count</code> and decide how to handle the request based on other
+ * information that it has, e.g. allow more requests to certain users based on roles, etc.
+ * </p>
+ * <p>
+ * <strong>WARNING:</strong> if Tomcat is behind a reverse proxy then you must make sure that the Rate Limit Filter sees
+ * the client IP address, so if for example you are using the <a href="#Remote_IP_Filter">Remote IP Filter</a>, then the
+ * filter mapping for the Rate Limit Filter must come <em>after</em> the mapping of the Remote IP Filter to ensure that
+ * each request has its IP address resolved before the Rate Limit Filter is applied. Failure to do so will count
+ * requests from different IPs in the same bucket and will result in a self inflicted DoS attack.
+ * </p>
  */
 public class RateLimitFilter extends GenericFilter {
 
@@ -199,16 +195,14 @@ public class RateLimitFilter extends GenericFilter {
 
         actualRequests = (int) Math.round(bucketCounter.getRatio() * bucketRequests);
 
-        log.info(sm.getString("rateLimitFilter.initialized",
-            super.getFilterName(), Integer.valueOf(bucketRequests), Integer.valueOf(bucketDuration),
-            Integer.valueOf(getActualRequests()), Integer.valueOf(getActualDurationInSeconds()),
-            (!enforce ? "Not " : "") + "enforcing")
-        );
+        log.info(sm.getString("rateLimitFilter.initialized", super.getFilterName(), Integer.valueOf(bucketRequests),
+                Integer.valueOf(bucketDuration), Integer.valueOf(getActualRequests()),
+                Integer.valueOf(getActualDurationInSeconds()), (!enforce ? "Not " : "") + "enforcing"));
     }
 
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-                            throws IOException, ServletException {
+            throws IOException, ServletException {
 
         String ipAddr = request.getRemoteAddr();
         int reqCount = bucketCounter.increment(ipAddr);
@@ -218,10 +212,9 @@ public class RateLimitFilter extends GenericFilter {
         if (enforce && (reqCount > actualRequests)) {
 
             ((HttpServletResponse) response).sendError(statusCode, statusMessage);
-            log.warn(sm.getString("rateLimitFilter.maxRequestsExceeded",
-                super.getFilterName(), Integer.valueOf(reqCount), ipAddr, Integer.valueOf(getActualRequests()),
-                Integer.valueOf(getActualDurationInSeconds()))
-            );
+            log.warn(sm.getString("rateLimitFilter.maxRequestsExceeded", super.getFilterName(),
+                    Integer.valueOf(reqCount), ipAddr, Integer.valueOf(getActualRequests()),
+                    Integer.valueOf(getActualDurationInSeconds())));
 
             return;
         }
diff --git a/java/org/apache/catalina/util/TimeBucketCounter.java b/java/org/apache/catalina/util/TimeBucketCounter.java
index 4c1974242e..9a472523c7 100644
--- a/java/org/apache/catalina/util/TimeBucketCounter.java
+++ b/java/org/apache/catalina/util/TimeBucketCounter.java
@@ -21,34 +21,29 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * this class maintains a thread safe hash map that has timestamp-based buckets
- * followed by a string for a key, and a counter for a value. each time the
- * increment() method is called it adds the key if it does not exist, increments
- * its value and returns it.
- *
- * a maintenance thread cleans up keys that are prefixed by previous timestamp
- * buckets.
+ * This class maintains a thread safe hash map that has timestamp-based buckets followed by a string for a key, and a
+ * counter for a value. each time the increment() method is called it adds the key if it does not exist, increments its
+ * value and returns it. a maintenance thread cleans up keys that are prefixed by previous timestamp buckets.
  */
 public class TimeBucketCounter {
 
     /**
      * Map to hold the buckets
      */
-    private final ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<String,AtomicInteger> map = new ConcurrentHashMap<>();
 
     /**
-     * Milliseconds bucket size as a Power of 2 for bit shift math, e.g.
-     * 16 for 65_536ms which is about 1:05 minute
+     * Milliseconds bucket size as a Power of 2 for bit shift math, e.g. 16 for 65_536ms which is about 1:05 minute
      */
     private final int numBits;
 
     /**
-     * ratio of actual duration to config duration
+     * Ratio of actual duration to config duration
      */
     private final double ratio;
 
     /**
-     * flag for the maintenance thread
+     * Flag for the maintenance thread
      */
     volatile boolean isRunning = false;
 
@@ -78,10 +73,10 @@ public class TimeBucketCounter {
     }
 
     /**
-     * increments the counter for the passed identifier in the current time
-     * bucket and returns the new value
+     * Increments the counter for the passed identifier in the current time bucket and returns the new value.
      *
      * @param identifier an identifier for which we want to maintain count, e.g. IP Address
+     *
      * @return the count within the current time bucket
      */
     public final int increment(String identifier) {
@@ -91,9 +86,8 @@ public class TimeBucketCounter {
     }
 
     /**
-     * calculates the current time bucket prefix by shifting bits for fast
-     * division, e.g. shift 16 bits is the same as dividing by 65,536 which is
-     * about 1:05m
+     * Calculates the current time bucket prefix by shifting bits for fast division, e.g. shift 16 bits is the same as
+     * dividing by 65,536 which is about 1:05m.
      *
      * @return The current bucket prefix.
      */
@@ -106,9 +100,8 @@ public class TimeBucketCounter {
     }
 
     /**
-     * the actual duration may differ from the configured duration because
-     * it is set to the next power of 2 value in order to perform very fast
-     * bit shift arithmetic
+     * The actual duration may differ from the configured duration because it is set to the next power of 2 value in
+     * order to perform very fast bit shift arithmetic.
      *
      * @return the actual bucket duration in milliseconds
      */
@@ -117,20 +110,18 @@ public class TimeBucketCounter {
     }
 
     /**
-     * returns the ratio between the configured duration param and the
-     * actual duration which will be set to the next power of 2.  we then
-     * multiply the configured requests param by the same ratio in order
-     * to compensate for the added time, if any
+     * Returns the ratio between the configured duration param and the actual duration which will be set to the next
+     * power of 2. We then multiply the configured requests param by the same ratio in order to compensate for the added
+     * time, if any.
      *
-     * @return the ratio, e.g. 1.092 if the actual duration is 65_536 for
-     *         the configured duration of 60_000
+     * @return the ratio, e.g. 1.092 if the actual duration is 65_536 for the configured duration of 60_000
      */
     public double getRatio() {
         return ratio;
     }
 
     /**
-     * returns the ratio to the next power of 2 so that we can adjust the value
+     * Returns the ratio to the next power of 2 so that we can adjust the value.
      */
     static double ratioToPowerOf2(int value) {
         double nextPO2 = nextPowerOf2(value);
@@ -138,8 +129,7 @@ public class TimeBucketCounter {
     }
 
     /**
-     * returns the next power of 2 given a value, e.g. 256 for 250,
-     * or 1024, for 1000
+     * Returns the next power of 2 given a value, e.g. 256 for 250, or 1024, for 1000.
      */
     static int nextPowerOf2(int value) {
         int valueOfHighestBit = Integer.highestOneBit(value);
@@ -151,8 +141,7 @@ public class TimeBucketCounter {
     }
 
     /**
-     * when we want to test a full bucket duration we need to sleep until the
-     * next bucket starts
+     * When we want to test a full bucket duration we need to sleep until the next bucket starts.
      *
      * @return the number of milliseconds until the next bucket
      */
@@ -164,14 +153,14 @@ public class TimeBucketCounter {
     }
 
     /**
-     * sets isRunning to false to terminate the maintenance thread
+     * Sets isRunning to false to terminate the maintenance thread.
      */
     public void destroy() {
         this.isRunning = false;
     }
 
     /**
-     * this class runs a background thread to clean up old keys from the map
+     * This class runs a background thread to clean up old keys from the map.
      */
     class MaintenanceThread extends Thread {
 
@@ -194,14 +183,15 @@ public class TimeBucketCounter {
 
             while (isRunning) {
                 String currentBucketPrefix = String.valueOf(getCurrentBucketPrefix());
-                ConcurrentHashMap.KeySetView<String, AtomicInteger> keys = map.keySet();
+                ConcurrentHashMap.KeySetView<String,AtomicInteger> keys = map.keySet();
 
                 // remove obsolete keys
                 keys.removeIf(k -> !k.startsWith(currentBucketPrefix));
 
                 try {
                     Thread.sleep(sleeptime);
-                } catch (InterruptedException e) {}
+                } catch (InterruptedException e) {
+                }
             }
         }
     }
diff --git a/test/org/apache/catalina/filters/TestRateLimitFilter.java b/test/org/apache/catalina/filters/TestRateLimitFilter.java
index a1b0b6a0bc..bf6c397972 100644
--- a/test/org/apache/catalina/filters/TestRateLimitFilter.java
+++ b/test/org/apache/catalina/filters/TestRateLimitFilter.java
@@ -73,15 +73,15 @@ public class TestRateLimitFilter extends TomcatBaseTest {
 
         Thread.sleep(5000);
 
-        Assert.assertEquals(200, tc1.results[24]);    // only 25 requests made, all allowed
+        Assert.assertEquals(200, tc1.results[24]); // only 25 requests made, all allowed
 
-        Assert.assertEquals(200, tc2.results[49]);    // only 25 requests made, all allowed
+        Assert.assertEquals(200, tc2.results[49]); // only 25 requests made, all allowed
 
         Assert.assertEquals(200, tc3.results[allowedRequests - 1]); // first allowedRequests allowed
-        Assert.assertEquals(429, tc3.results[allowedRequests]);     // subsequent requests dropped
+        Assert.assertEquals(429, tc3.results[allowedRequests]); // subsequent requests dropped
 
         Assert.assertEquals(200, tc4.results[allowedRequests - 1]); // first allowedRequests allowed
-        Assert.assertEquals(429, tc4.results[allowedRequests]);     // subsequent requests dropped
+        Assert.assertEquals(429, tc4.results[allowedRequests]); // subsequent requests dropped
     }
 
     private RateLimitFilter testRateLimitFilter(FilterDef filterDef, Context root) throws ServletException {
@@ -102,7 +102,6 @@ public class TestRateLimitFilter extends TomcatBaseTest {
         rateLimitFilter.init(filterConfig);
 
         return rateLimitFilter;
-        //*/
     }
 
     static class TestClient extends Thread {
@@ -140,8 +139,7 @@ public class TestRateLimitFilter extends TomcatBaseTest {
                             Integer.valueOf(response.getStatus()));
                     Thread.sleep(sleep);
                 }
-            }
-            catch (Exception ex) {
+            } catch (Exception ex) {
                 ex.printStackTrace();
             }
         }
@@ -167,7 +165,7 @@ public class TestRateLimitFilter extends TomcatBaseTest {
     private static FilterConfig generateFilterConfig(FilterDef filterDef) {
 
         TesterServletContext mockServletContext = new TesterServletContext();
-        Map<String, String> parameters = filterDef.getParameterMap();
+        Map<String,String> parameters = filterDef.getParameterMap();
 
         FilterConfig filterConfig = new FilterConfig() {
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org