This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit b4f09a0082acac7da9842d4cb003ff752917915e 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 | 90 ++++++++++------------ .../apache/catalina/util/TimeBucketCounter.java | 48 +++++------- .../catalina/filters/TestRateLimitFilter.java | 14 ++-- 3 files changed, 66 insertions(+), 86 deletions(-) diff --git a/java/org/apache/catalina/filters/RateLimitFilter.java b/java/org/apache/catalina/filters/RateLimitFilter.java index f3ded50cf0..2b35243e84 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 "429 Too many requests" 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 "429 Too many requests" 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 implements Filter { @@ -199,16 +195,14 @@ public class RateLimitFilter implements Filter { actualRequests = (int) Math.round(bucketCounter.getRatio() * bucketRequests); - log.info(sm.getString("rateLimitFilter.initialized", - filterName, Integer.valueOf(bucketRequests), Integer.valueOf(bucketDuration), - Integer.valueOf(getActualRequests()), Integer.valueOf(getActualDurationInSeconds()), - (!enforce ? "Not " : "") + "enforcing") - ); + log.info(sm.getString("rateLimitFilter.initialized", filterName, 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,8 @@ public class RateLimitFilter implements Filter { if (enforce && (reqCount > actualRequests)) { ((HttpServletResponse) response).sendError(statusCode, statusMessage); - log.warn(sm.getString("rateLimitFilter.maxRequestsExceeded", - filterName, Integer.valueOf(reqCount), ipAddr, Integer.valueOf(getActualRequests()), - Integer.valueOf(getActualDurationInSeconds())) - ); + log.warn(sm.getString("rateLimitFilter.maxRequestsExceeded", filterName, 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 d104eebf4e..ef93f51665 100644 --- a/java/org/apache/catalina/util/TimeBucketCounter.java +++ b/java/org/apache/catalina/util/TimeBucketCounter.java @@ -22,24 +22,19 @@ 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; @@ -79,10 +74,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) { @@ -100,9 +95,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. */ @@ -115,9 +109,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 */ @@ -126,13 +119,11 @@ 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; @@ -147,8 +138,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); @@ -160,8 +150,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 */ @@ -214,7 +203,8 @@ public class TimeBucketCounter { 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 684bd3b258..aa6ffd0c48 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) { final TesterServletContext mockServletContext = new TesterServletContext(); - final Map<String, String> parameters = filterDef.getParameterMap(); + final Map<String,String> parameters = filterDef.getParameterMap(); FilterConfig filterConfig = new FilterConfig() { --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org