From ca66322939a308ace2b1794f98a569e4bb4e9f38 Mon Sep 17 00:00:00 2001 From: Marcos Barbero Date: Sun, 15 Sep 2019 10:48:40 +0200 Subject: [PATCH 1/3] Add URL Regex Pattern rate limit (#194) * Add URL_PATTERN type * Upgrade maven version * Upgrade spring-boot version * Update documentation --- .mvn/wrapper/MavenWrapperDownloader.java | 114 ++++++++++++++++++ .mvn/wrapper/maven-wrapper.jar | Bin 47610 -> 48337 bytes .mvn/wrapper/maven-wrapper.properties | 2 +- .travis.yml | 4 +- README.adoc | 39 +++++- mvnw | 67 +++++++++- mvnw.cmd | 22 +++- pom.xml | 11 +- spring-cloud-starter-zuul-ratelimit/pom.xml | 2 +- spring-cloud-zuul-ratelimit-core/pom.xml | 2 +- .../properties/RateLimitProperties.java | 9 ++ .../config/properties/RateLimitType.java | 37 +++++- .../validators/PoliciesValidator.java | 8 +- .../filters/AbstractRateLimitFilter.java | 2 +- .../config/properties/RateLimitTypeTest.java | 26 ++++ .../validators/PoliciesValidatorTest.java | 11 ++ .../RateLimitExceededExceptionTest.java | 2 +- spring-cloud-zuul-ratelimit-coverage/pom.xml | 2 +- .../pom.xml | 2 +- .../bucket4j-hazelcast/pom.xml | 2 +- .../bucket4j-ignite/pom.xml | 2 +- .../bucket4j-infinispan/pom.xml | 2 +- .../bucket4j-jcache/pom.xml | 2 +- .../tests/Bucket4jJCacheApplication.java | 7 +- .../src/main/resources/application.properties | 42 +++++++ .../src/main/resources/application.yml | 47 -------- .../it/Bucket4jJCacheApplicationTestIT.java | 54 ++++++--- .../consul/pom.xml | 3 +- .../redis/pom.xml | 2 +- .../security-context/pom.xml | 2 +- .../springdata/pom.xml | 2 +- 31 files changed, 430 insertions(+), 99 deletions(-) create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.properties delete mode 100644 spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.yml diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..72308aa4 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,114 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.util.Properties; + +public class MavenWrapperDownloader { + + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = + "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: : " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index 9cc84ea9b4d95453115d0c26488d6a78694e0bc6..01e67997377a393fd672c7dcde9dccbedf0cb1e9 100644 GIT binary patch delta 10107 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 P`1e%9f`Lga`w#s;Q$rWl diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index b573bb50..cd0d451c 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip diff --git a/.travis.yml b/.travis.yml index 961f4e40..cb9bc313 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: java +dist: trusty + sudo: false addons: @@ -41,7 +43,7 @@ deploy: on: branch: master repo: marcosbarbero/spring-cloud-zuul-ratelimit - jdk: oraclejdk8 + jdk: oraclejdk9 env: global: diff --git a/README.adoc b/README.adoc index 84ac39b3..8b8774b1 100644 --- a/README.adoc +++ b/README.adoc @@ -14,6 +14,8 @@ There are five built-in rate limit approaches: ** Uses the user origin request * URL ** Uses the request path of the downstream service + * URL Pattern + ** Uses the request Ant path pattern to the downstream service * ROLE ** Uses the authenticated user roles * Request method @@ -147,7 +149,7 @@ Add the following dependency accordingly to the chosen data storage: ---- -Sample configuration +Sample YAML configuration [source, yaml] ---- zuul: @@ -183,6 +185,41 @@ zuul: - httpmethod=get #case insensitive ---- +Sample Properties configuration +[source, properties] +---- +zuul.ratelimit.enabled=true +zuul.ratelimit.key-prefix=your-prefix +zuul.ratelimit.repository=REDIS +zuul.ratelimit.behind-proxy=true +zuul.ratelimit.add-response-headers=true + +zuul.ratelimit.default-policy-list[0].limit=10 +zuul.ratelimit.default-policy-list[0].quota=1000 +zuul.ratelimit.default-policy-list[0].refresh-interval=60 + +# Adding multiple rate limit type +zuul.ratelimit.default-policy-list[0].type[0]=user +zuul.ratelimit.default-policy-list[0].type[1]=origin +zuul.ratelimit.default-policy-list[0].type[2]=url +zuul.ratelimit.default-policy-list[0].type[3]=httpmethod + +# Adding the first rate limit policy to "myServiceId" +zuul.ratelimit.policy-list.myServiceId[0].limit=10 +zuul.ratelimit.policy-list.myServiceId[0].quota=1000 +zuul.ratelimit.policy-list.myServiceId[0].refresh-interval=60 +zuul.ratelimit.policy-list.myServiceId[0].type[0]=user +zuul.ratelimit.policy-list.myServiceId[0].type[1]=origin +zuul.ratelimit.policy-list.myServiceId[0].type[2]=url + +# Adding the second rate limit policy to "myServiceId" +zuul.ratelimit.policy-list.myServiceId[1].type[0]=user=anonymous +zuul.ratelimit.policy-list.myServiceId[1].type[1]=origin=somemachine.com +zuul.ratelimit.policy-list.myServiceId[1].type[2]=url_pattern=/api/*/payment +zuul.ratelimit.policy-list.myServiceId[1].type[3]=role=user +zuul.ratelimit.policy-list.myServiceId[1].type[4]=httpmethod=get +---- + == Available implementations There are eight implementations provided: diff --git a/mvnw b/mvnw index 5bf251c0..8b9da3b8 100755 --- a/mvnw +++ b/mvnw @@ -8,7 +8,7 @@ # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an @@ -108,7 +108,7 @@ if $cygwin ; then CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi -# For Migwn, ensure paths are in UNIX format before anything is touched +# For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" @@ -200,8 +200,69 @@ if [ -z "$BASE_DIR" ]; then exit 1; fi +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -echo $MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java diff --git a/mvnw.cmd b/mvnw.cmd index 019bd74d..fef5a8f7 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -7,7 +7,7 @@ @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM -@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM https://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @@ -35,6 +35,8 @@ @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off +@REM set title of command window +title %0 @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @@ -115,10 +117,26 @@ for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do s :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" +FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end diff --git a/pom.xml b/pom.xml index d518bae1..d05b5491 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.9.RELEASE + 2.1.8.RELEASE com.marcosbarbero.cloud @@ -19,7 +19,7 @@ ]]> https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit - 2.2.4.RELEASE + 2.2.5.RELEASE @@ -30,9 +30,9 @@ - 1.8 - 1.8 1.8 + ${java.version} + ${java.version} 1.6.8 0.8.4 4.3.0 @@ -44,6 +44,7 @@ ${project.build.directory}/coverage-reports/jacoco.exec com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit + Greenwich.SR3 @@ -83,7 +84,7 @@ org.springframework.cloud spring-cloud-dependencies - Greenwich.SR2 + ${spring-cloud.version} pom import diff --git a/spring-cloud-starter-zuul-ratelimit/pom.xml b/spring-cloud-starter-zuul-ratelimit/pom.xml index 8f143048..35bc317c 100644 --- a/spring-cloud-starter-zuul-ratelimit/pom.xml +++ b/spring-cloud-starter-zuul-ratelimit/pom.xml @@ -5,7 +5,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE .. 4.0.0 diff --git a/spring-cloud-zuul-ratelimit-core/pom.xml b/spring-cloud-zuul-ratelimit-core/pom.xml index b6908f9d..a11e2037 100644 --- a/spring-cloud-zuul-ratelimit-core/pom.xml +++ b/spring-cloud-zuul-ratelimit-core/pom.xml @@ -6,7 +6,7 @@ com.marcosbarbero.cloud spring-cloud-zuul-ratelimit-parent - 2.2.4.RELEASE + 2.2.5.RELEASE .. diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java index 904e102b..c45134b2 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java @@ -52,19 +52,27 @@ public class RateLimitProperties { @NotNull @Policies private List defaultPolicyList = Lists.newArrayList(); + @Valid @NotNull @Policies private Map> policyList = Maps.newHashMap(); + private boolean behindProxy; + private boolean enabled; + private boolean addResponseHeaders = true; + @NotNull @Value("${spring.application.name:rate-limit-application}") private String keyPrefix; + @NotNull private RateLimitRepository repository; + private int postFilterOrder = SEND_RESPONSE_FILTER_ORDER - 10; + private int preFilterOrder = FORM_BODY_WRAPPER_FILTER_ORDER; public List getPolicies(String key) { @@ -207,6 +215,7 @@ public static class MatchType { @Valid @NotNull private RateLimitType type; + private String matcher; public MatchType(@Valid @NotNull RateLimitType type, String matcher) { diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitType.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitType.java index 6e32d1b1..d557cbc0 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitType.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitType.java @@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.net.util.SubnetUtils; import org.springframework.cloud.netflix.zuul.filters.Route; +import org.springframework.util.AntPathMatcher; import javax.servlet.http.HttpServletRequest; import java.util.Optional; @@ -87,6 +88,11 @@ public boolean apply(HttpServletRequest request, Route route, RateLimitUtils rat public String key(HttpServletRequest request, Route route, RateLimitUtils rateLimitUtils, String matcher) { return matcher; } + + @Override + public boolean isValid(String matcher) { + return StringUtils.isNotEmpty(matcher); + } }, /** @@ -103,11 +109,40 @@ public String key(HttpServletRequest request, Route route, RateLimitUtils rateLi return StringUtils.isEmpty(matcher) ? request.getMethod() : "http-method"; } }, - ; + + /** + * Rate limit policy considering an URL Pattern + */ + URL_PATTERN { + @Override + public boolean apply(HttpServletRequest request, Route route, RateLimitUtils rateLimitUtils, String matcher) { + return new AntPathMatcher().match(matcher.toLowerCase(), request.getRequestURI().toLowerCase()); + } + + @Override + public String key(HttpServletRequest request, Route route, RateLimitUtils rateLimitUtils, String matcher) { + return matcher; + } + + @Override + public boolean isValid(String matcher) { + return StringUtils.isNotEmpty(matcher); + } + }; public abstract boolean apply(HttpServletRequest request, Route route, RateLimitUtils rateLimitUtils, String matcher); public abstract String key(HttpServletRequest request, Route route, RateLimitUtils rateLimitUtils, String matcher); + + /** + * Helper method to validate specific cases per type. + * + * @param matcher The type matcher + * @return The default behavior will always return true. + */ + public boolean isValid(String matcher) { + return true; + } } diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidator.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidator.java index f33a0626..3d3b5ffd 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidator.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidator.java @@ -23,8 +23,6 @@ import java.util.Collection; import java.util.Map; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.properties.RateLimitType.ROLE; - /** * Validates the rate limit policies. * @@ -60,11 +58,11 @@ private boolean isValidObject(Object o) { } private boolean isValidPolicy(Policy policy) { - return (policy.getLimit() != null || policy.getQuota() != null) && isValidRoles(policy); + return (policy.getLimit() != null || policy.getQuota() != null) && isValid(policy); } - private boolean isValidRoles(Policy policy) { + private boolean isValid(Policy policy) { return policy.getType().stream() - .noneMatch(type -> type.getType().equals(ROLE) && type.getMatcher() == null); + .allMatch(type -> type.getType().isValid(type.getMatcher())); } } diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java index b381e10f..0996e58c 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java @@ -35,7 +35,7 @@ * @author Marcos Barbero * @author Liel Chayoun */ -public abstract class AbstractRateLimitFilter extends ZuulFilter { +abstract class AbstractRateLimitFilter extends ZuulFilter { private final RateLimitProperties properties; private final RouteLocator routeLocator; diff --git a/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitTypeTest.java b/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitTypeTest.java index 576f8f8a..78c11450 100644 --- a/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitTypeTest.java +++ b/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitTypeTest.java @@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletRequest; import java.util.Collections; +import java.util.concurrent.ThreadLocalRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; @@ -89,6 +90,31 @@ public void applyURL() { assertThat(apply).isTrue(); } + @Test + public void applyPatternURL() { + int id = ThreadLocalRandom.current().nextInt(0, 5000); + + when(httpServletRequest.getRequestURI()).thenReturn("/resource/" + id + "/specific"); + + boolean apply = RateLimitType.URL_PATTERN.apply(httpServletRequest, route, rateLimitUtils, "/resource/*/specific"); + assertThat(apply).isTrue(); + } + + @Test + public void keyPatternURL() { + String pattern = "/resource/*/specific"; + String key = RateLimitType.URL_PATTERN.key(httpServletRequest, route, rateLimitUtils, pattern); + assertThat(key).isEqualTo(pattern); + } + + @Test + public void applyPatternURL_withInvalidPattern_shouldNotApply() { + when(httpServletRequest.getRequestURI()).thenReturn("/resource/abcd/specific"); + + boolean apply = RateLimitType.URL_PATTERN.apply(httpServletRequest, route, rateLimitUtils, "/resource/??/specific"); + assertThat(apply).isFalse(); + } + @Test public void applyURLNoMatch() { boolean apply = RateLimitType.URL.apply(httpServletRequest, route, rateLimitUtils, "/other"); diff --git a/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidatorTest.java b/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidatorTest.java index 82a988a8..ddcb9ef3 100644 --- a/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidatorTest.java +++ b/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/validators/PoliciesValidatorTest.java @@ -93,6 +93,17 @@ public void testValidOnPolicyWithLimitAndRole() { assertThat(violations).isEmpty(); } + @Test + public void testValidOnPolicyWithLimitAndURLPattern() { + properties.setKeyPrefix("prefix"); + Policy policy = getPolicy(1L, null); + policy.getType().add(new Policy.MatchType(RateLimitType.URL_PATTERN, "/user/[0-9]+")); + properties.getDefaultPolicyList().add(policy); + properties.getPolicyList().put("key", Lists.newArrayList(policy)); + Set> violations = validator.validate(properties); + assertThat(violations).isEmpty(); + } + @Test public void testValidOnPolicyWithLimitAndRoleWithoutMatcher() { properties.setKeyPrefix("prefix"); diff --git a/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitExceededExceptionTest.java b/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitExceededExceptionTest.java index 4ad3a40e..94f64e21 100644 --- a/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitExceededExceptionTest.java +++ b/spring-cloud-zuul-ratelimit-core/src/test/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitExceededExceptionTest.java @@ -28,6 +28,6 @@ public void testExceptionInfo() { assertThat(cause).isInstanceOf(ZuulException.class); ZuulException zuulException = (ZuulException) cause; - assertThat(zuulException.getMessage()).isEqualTo("429"); + assertThat(zuulException.getMessage()).contains("429"); } } \ No newline at end of file diff --git a/spring-cloud-zuul-ratelimit-coverage/pom.xml b/spring-cloud-zuul-ratelimit-coverage/pom.xml index 11729777..a50a0584 100644 --- a/spring-cloud-zuul-ratelimit-coverage/pom.xml +++ b/spring-cloud-zuul-ratelimit-coverage/pom.xml @@ -5,7 +5,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../pom.xml diff --git a/spring-cloud-zuul-ratelimit-dependencies/pom.xml b/spring-cloud-zuul-ratelimit-dependencies/pom.xml index d7da6b22..ec90d0c3 100644 --- a/spring-cloud-zuul-ratelimit-dependencies/pom.xml +++ b/spring-cloud-zuul-ratelimit-dependencies/pom.xml @@ -7,7 +7,7 @@ com.marcosbarbero.cloud spring-cloud-zuul-ratelimit-dependencies - 2.2.4.RELEASE + 2.2.5.RELEASE pom https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-hazelcast/pom.xml b/spring-cloud-zuul-ratelimit-tests/bucket4j-hazelcast/pom.xml index 431143ed..493065d9 100644 --- a/spring-cloud-zuul-ratelimit-tests/bucket4j-hazelcast/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/bucket4j-hazelcast/pom.xml @@ -6,7 +6,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-ignite/pom.xml b/spring-cloud-zuul-ratelimit-tests/bucket4j-ignite/pom.xml index 1f4add19..90c8b78d 100644 --- a/spring-cloud-zuul-ratelimit-tests/bucket4j-ignite/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/bucket4j-ignite/pom.xml @@ -6,7 +6,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-infinispan/pom.xml b/spring-cloud-zuul-ratelimit-tests/bucket4j-infinispan/pom.xml index d4505d40..a41f2bb0 100644 --- a/spring-cloud-zuul-ratelimit-tests/bucket4j-infinispan/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/bucket4j-infinispan/pom.xml @@ -6,7 +6,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/pom.xml b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/pom.xml index 7109729c..454bdadf 100644 --- a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/pom.xml @@ -6,7 +6,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/java/com/marcosbarbero/tests/Bucket4jJCacheApplication.java b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/java/com/marcosbarbero/tests/Bucket4jJCacheApplication.java index 0b3d0ce8..c7541486 100644 --- a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/java/com/marcosbarbero/tests/Bucket4jJCacheApplication.java +++ b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/java/com/marcosbarbero/tests/Bucket4jJCacheApplication.java @@ -44,7 +44,7 @@ public void destroy() { } @RestController - public class ServiceController { + public static class ServiceController { public static final String RESPONSE_BODY = "ResponseBody"; @@ -73,5 +73,10 @@ public ResponseEntity serviceE() throws InterruptedException { Thread.sleep(1100); return ResponseEntity.ok(RESPONSE_BODY); } + + @GetMapping("/serviceF/{paramName}/specific") + public ResponseEntity serviceF(@PathVariable Integer paramName) { + return ResponseEntity.ok(RESPONSE_BODY + " " + paramName); + } } } diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.properties b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.properties new file mode 100644 index 00000000..df2c119d --- /dev/null +++ b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.properties @@ -0,0 +1,42 @@ +zuul.routes.serviceA.path=/serviceA +zuul.routes.serviceA.url=forward:/ + +zuul.routes.serviceB.path=/serviceB +zuul.routes.serviceB.url=forward:/ + +zuul.routes.serviceC.path=/serviceC +zuul.routes.serviceC.url=forward:/ + +zuul.routes.serviceD.path=/serviceD/** +zuul.routes.serviceD.url=forward:/ +zuul.routes.serviceD.strip-prefix=false + +zuul.routes.serviceE.path=/serviceE +zuul.routes.serviceE.url=forward:/ + +zuul.routes.serviceF.path=/serviceF/** +zuul.routes.serviceF.url=forward:/ +zuul.routes.serviceF.strip-prefix=false + +zuul.ratelimit.enabled=true +zuul.ratelimit.repository=BUCKET4J_JCACHE + +zuul.ratelimit.policy-list.serviceA[0].limit=10 +zuul.ratelimit.policy-list.serviceA[0].refresh-interval=60 +zuul.ratelimit.policy-list.serviceA[0].type[0]=origin + +zuul.ratelimit.policy-list.serviceB[0].limit=2 +zuul.ratelimit.policy-list.serviceB[0].refresh-interval=2 +zuul.ratelimit.policy-list.serviceB[0].type[0]=origin + +zuul.ratelimit.policy-list.serviceD[0].limit=2 +zuul.ratelimit.policy-list.serviceD[0].refresh-interval=60 +zuul.ratelimit.policy-list.serviceD[0].type[0]=url + +zuul.ratelimit.policy-list.serviceE[0].quota=1 +zuul.ratelimit.policy-list.serviceE[0].refresh-interval=60 +zuul.ratelimit.policy-list.serviceE[0].type[0]=origin + +zuul.ratelimit.policy-list.serviceF[0].limit=2 +zuul.ratelimit.policy-list.serviceF[0].refresh-interval=60 +zuul.ratelimit.policy-list.serviceF[0].type[0]=url_pattern=/serviceF/*/specific \ No newline at end of file diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.yml b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.yml deleted file mode 100644 index 4dde1527..00000000 --- a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/main/resources/application.yml +++ /dev/null @@ -1,47 +0,0 @@ -zuul: - routes: - serviceA: - path: /serviceA - url: forward:/ - serviceB: - path: /serviceB - url: forward:/ - serviceC: - path: /serviceC - url: forward:/ - serviceD: - strip-prefix: false - path: /serviceD/** - url: forward:/ - serviceE: - path: /serviceE - url: forward:/ - ratelimit: - enabled: true - repository: BUCKET4J_JCACHE - policy-list: - serviceA: - - limit: 10 - refresh-interval: 60 - type: - - origin - serviceB: - - limit: 2 - refresh-interval: 2 - type: - - origin - serviceD: - - limit: 2 - refresh-interval: 60 - type: - - url - serviceE: - - quota: 1 - refresh-interval: 60 - type: - - origin - strip-prefix: true - -logging: - level: - ROOT: error \ No newline at end of file diff --git a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/test/java/com/marcosbarbero/tests/it/Bucket4jJCacheApplicationTestIT.java b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/test/java/com/marcosbarbero/tests/it/Bucket4jJCacheApplicationTestIT.java index fd2e81aa..5130d6ad 100644 --- a/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/test/java/com/marcosbarbero/tests/it/Bucket4jJCacheApplicationTestIT.java +++ b/spring-cloud-zuul-ratelimit-tests/bucket4j-jcache/src/test/java/com/marcosbarbero/tests/it/Bucket4jJCacheApplicationTestIT.java @@ -4,7 +4,6 @@ import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.properties.RateLimitProperties; import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.repository.bucket4j.Bucket4jJCacheRateLimiter; import com.marcosbarbero.tests.Bucket4jJCacheApplication; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,22 +11,16 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_LIMIT; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_QUOTA; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_REMAINING; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_REMAINING_QUOTA; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_RESET; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.*; +import static org.junit.Assert.*; import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; @@ -61,7 +54,7 @@ public void testKeyPrefixDefaultValue() { public void testNotExceedingCapacityRequest() { ResponseEntity response = this.restTemplate.getForEntity("/serviceA", String.class); HttpHeaders headers = response.getHeaders(); - assertHeaders(headers, "rate-limit-application_serviceA_127.0.0.1",false, false); + assertHeaders(headers, "rate-limit-application_serviceA_127.0.0.1", false, false); assertEquals(OK, response.getStatusCode()); } @@ -84,7 +77,7 @@ public void testExceedingCapacity() throws InterruptedException { response = this.restTemplate.getForEntity("/serviceB", String.class); headers = response.getHeaders(); - assertHeaders(headers, key,false, false); + assertHeaders(headers, key, false, false); assertEquals(OK, response.getStatusCode()); } @@ -92,7 +85,7 @@ public void testExceedingCapacity() throws InterruptedException { public void testNoRateLimit() { ResponseEntity response = this.restTemplate.getForEntity("/serviceC", String.class); HttpHeaders headers = response.getHeaders(); - assertHeaders(headers, "rate-limit-application_serviceC",true, false); + assertHeaders(headers, "rate-limit-application_serviceC", true, false); assertEquals(OK, response.getStatusCode()); } @@ -108,22 +101,49 @@ public void testMultipleUrls() { ResponseEntity response = this.restTemplate.getForEntity("/serviceD/" + randomPath, String.class); HttpHeaders headers = response.getHeaders(); - assertHeaders(headers, "rate-limit-application_serviceD_serviceD_" + randomPath,false, false); + assertHeaders(headers, "rate-limit-application_serviceD_serviceD_" + randomPath, false, false); assertEquals(OK, response.getStatusCode()); } } + @Test + public void testMultipleUrlPattern() { + int randomInt = randomInt(); + + for (int i = 0; i < 5; i++) { + + if (i % 2 == 0) { + randomInt = randomInt(); + } + + ResponseEntity response = this.restTemplate.getForEntity("/serviceF/" + randomInt + "/specific", String.class); + HttpHeaders headers = response.getHeaders(); + + assertHeaders(headers, "rate-limit-application_serviceF_serviceF__specific_serviceF__specific", false, false); + + HttpStatus httpStatus = OK; + if (i > 1) { + httpStatus = TOO_MANY_REQUESTS; + } + assertEquals(httpStatus, response.getStatusCode()); + } + } + + private static int randomInt() { + return ThreadLocalRandom.current().nextInt(0, 5000); + } + @Test public void testExceedingQuotaCapacityRequest() { ResponseEntity response = this.restTemplate.getForEntity("/serviceE", String.class); HttpHeaders headers = response.getHeaders(); String key = "rate-limit-application_serviceE_127.0.0.1"; - assertHeaders(headers, key,false, true); + assertHeaders(headers, key, false, true); assertEquals(OK, response.getStatusCode()); response = this.restTemplate.getForEntity("/serviceE", String.class); headers = response.getHeaders(); - assertHeaders(headers, key,false, true); + assertHeaders(headers, key, false, true); assertEquals(TOO_MANY_REQUESTS, response.getStatusCode()); } diff --git a/spring-cloud-zuul-ratelimit-tests/consul/pom.xml b/spring-cloud-zuul-ratelimit-tests/consul/pom.xml index 7dadd839..73a08efd 100644 --- a/spring-cloud-zuul-ratelimit-tests/consul/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/consul/pom.xml @@ -5,7 +5,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml 4.0.0 @@ -13,7 +13,6 @@ consul Tests - Consul RateLimit - org.springframework.cloud diff --git a/spring-cloud-zuul-ratelimit-tests/redis/pom.xml b/spring-cloud-zuul-ratelimit-tests/redis/pom.xml index 25132908..4b814e4e 100644 --- a/spring-cloud-zuul-ratelimit-tests/redis/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/redis/pom.xml @@ -5,7 +5,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml diff --git a/spring-cloud-zuul-ratelimit-tests/security-context/pom.xml b/spring-cloud-zuul-ratelimit-tests/security-context/pom.xml index f1a79d74..cb5f8685 100644 --- a/spring-cloud-zuul-ratelimit-tests/security-context/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/security-context/pom.xml @@ -5,7 +5,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml diff --git a/spring-cloud-zuul-ratelimit-tests/springdata/pom.xml b/spring-cloud-zuul-ratelimit-tests/springdata/pom.xml index 9bf86047..75615d33 100644 --- a/spring-cloud-zuul-ratelimit-tests/springdata/pom.xml +++ b/spring-cloud-zuul-ratelimit-tests/springdata/pom.xml @@ -6,7 +6,7 @@ spring-cloud-zuul-ratelimit-parent com.marcosbarbero.cloud - 2.2.4.RELEASE + 2.2.5.RELEASE ../../pom.xml From 85e39e424f6f3e0fbb97560d2efb3a7f084062e5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2019 08:40:52 +0200 Subject: [PATCH 2/3] Bump ignite-core from 2.7.5 to 2.7.6 (#197) Bumps ignite-core from 2.7.5 to 2.7.6. Signed-off-by: dependabot-preview[bot] --- spring-cloud-zuul-ratelimit-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-zuul-ratelimit-core/pom.xml b/spring-cloud-zuul-ratelimit-core/pom.xml index a11e2037..e2770b31 100644 --- a/spring-cloud-zuul-ratelimit-core/pom.xml +++ b/spring-cloud-zuul-ratelimit-core/pom.xml @@ -23,7 +23,7 @@ ${basedir}/.. 4.4.1 3.12.2 - 2.7.5 + 2.7.6 9.4.16.Final From 80e59840878445b7932ba4005d5ca722dc3e2b8f Mon Sep 17 00:00:00 2001 From: Marcos Barbero Date: Fri, 20 Sep 2019 10:55:23 +0200 Subject: [PATCH 3/3] Performance enhancement (#198) --- .../ratelimit/RateLimitAutoConfiguration.java | 8 ++--- .../properties/RateLimitProperties.java | 3 -- .../config/repository/RedisRateLimiter.java | 2 +- .../filters/AbstractRateLimitFilter.java | 35 ++++++++++++++++--- .../ratelimit/filters/RateLimitPreFilter.java | 9 ++--- .../support/DefaultRateLimitUtils.java | 3 +- .../ratelimit/support/RateLimitConstants.java | 3 ++ .../support/StringToMatchTypeConverter.java | 2 +- 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/RateLimitAutoConfiguration.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/RateLimitAutoConfiguration.java index effcd771..4412e460 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/RateLimitAutoConfiguration.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/RateLimitAutoConfiguration.java @@ -78,7 +78,7 @@ @ConditionalOnProperty(prefix = PREFIX, name = "enabled", havingValue = "true") public class RateLimitAutoConfiguration { - private final UrlPathHelper urlPathHelper = new UrlPathHelper(); + private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper(); @Bean @ConfigurationPropertiesBinding @@ -96,7 +96,7 @@ public RateLimiterErrorHandler rateLimiterErrorHandler() { public ZuulFilter rateLimiterPreFilter(final RateLimiter rateLimiter, final RateLimitProperties rateLimitProperties, final RouteLocator routeLocator, final RateLimitKeyGenerator rateLimitKeyGenerator, final RateLimitUtils rateLimitUtils) { - return new RateLimitPreFilter(rateLimitProperties, routeLocator, urlPathHelper, rateLimiter, + return new RateLimitPreFilter(rateLimitProperties, routeLocator, URL_PATH_HELPER, rateLimiter, rateLimitKeyGenerator, rateLimitUtils); } @@ -104,7 +104,7 @@ public ZuulFilter rateLimiterPreFilter(final RateLimiter rateLimiter, final Rate public ZuulFilter rateLimiterPostFilter(final RateLimiter rateLimiter, final RateLimitProperties rateLimitProperties, final RouteLocator routeLocator, final RateLimitKeyGenerator rateLimitKeyGenerator, final RateLimitUtils rateLimitUtils) { - return new RateLimitPostFilter(rateLimitProperties, routeLocator, urlPathHelper, rateLimiter, + return new RateLimitPostFilter(rateLimitProperties, routeLocator, URL_PATH_HELPER, rateLimiter, rateLimitKeyGenerator, rateLimitUtils); } @@ -214,7 +214,7 @@ public RateLimiter bucket4jInfinispanRateLimiter(@Qualifier("RateLimit") final R @EntityScan @Configuration - @EnableJpaRepositories + @EnableJpaRepositories(basePackages = "com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.repository.springdata") @ConditionalOnMissingBean(RateLimiter.class) @ConditionalOnProperty(prefix = PREFIX, name = "repository", havingValue = "JPA") public static class SpringDataConfiguration { diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java index c45134b2..2e2c5f99 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/properties/RateLimitProperties.java @@ -76,9 +76,6 @@ public class RateLimitProperties { private int preFilterOrder = FORM_BODY_WRAPPER_FILTER_ORDER; public List getPolicies(String key) { - if (StringUtils.isEmpty(key)) { - return defaultPolicyList; - } return policyList.getOrDefault(key, defaultPolicyList); } diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/repository/RedisRateLimiter.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/repository/RedisRateLimiter.java index f3aa2152..b4b12af6 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/repository/RedisRateLimiter.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/config/repository/RedisRateLimiter.java @@ -73,7 +73,7 @@ private Long calcRemaining(Long limit, Long refreshInterval, long usage, String msg = "Failed retrieving rate for " + key + ", will return the current value"; rateLimiterErrorHandler.handleError(msg, e); } - return Math.max(-1, limit - current); + return Math.max(-1, limit - (current != null ? current : 0L)); } private void handleExpiration(String key, Long refreshInterval) { diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java index 0996e58c..e92b2bda 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/AbstractRateLimitFilter.java @@ -28,9 +28,11 @@ import javax.servlet.http.HttpServletRequest; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; +import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.CURRENT_REQUEST_POLICY; +import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.CURRENT_REQUEST_ROUTE; + /** * @author Marcos Barbero * @author Liel Chayoun @@ -58,16 +60,41 @@ public boolean shouldFilter() { } Route route(HttpServletRequest request) { + Route route = (Route) RequestContext.getCurrentContext().get(CURRENT_REQUEST_ROUTE); + if (route != null) { + return route; + } + String requestURI = urlPathHelper.getPathWithinApplication(request); - return routeLocator.getMatchingRoute(requestURI); + route = routeLocator.getMatchingRoute(requestURI); + + addObjectToRequest(CURRENT_REQUEST_ROUTE, route); + + return route; } + @SuppressWarnings("unchecked") protected List policy(Route route, HttpServletRequest request) { - String routeId = Optional.ofNullable(route).map(Route::getId).orElse(null); + List policies = (List) RequestContext.getCurrentContext().get(CURRENT_REQUEST_POLICY); + if (policies != null) { + return policies; + } + + String routeId = route != null ? route.getId() : null; alreadyLimited = false; - return properties.getPolicies(routeId).stream() + policies = properties.getPolicies(routeId).stream() .filter(policy -> applyPolicy(request, route, policy)) .collect(Collectors.toList()); + + addObjectToRequest(CURRENT_REQUEST_POLICY, policies); + + return policies; + } + + private void addObjectToRequest(String key, Object object) { + if (object != null) { + RequestContext.getCurrentContext().put(key, object); + } } private boolean applyPolicy(HttpServletRequest request, Route route, Policy policy) { diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/RateLimitPreFilter.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/RateLimitPreFilter.java index f2b00f6e..927072ae 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/RateLimitPreFilter.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/filters/RateLimitPreFilter.java @@ -16,12 +16,7 @@ package com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.filters; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_LIMIT; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_QUOTA; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_REMAINING; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_REMAINING_QUOTA; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.HEADER_RESET; -import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.REQUEST_START_TIME; +import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitConstants.*; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; @@ -112,7 +107,7 @@ public Object run() { if ((limit != null && remaining < 0) || (quota != null && remainingQuota < 0)) { ctx.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value()); - ctx.put("rateLimitExceeded", "true"); + ctx.put(RATE_LIMIT_EXCEEDED, "true"); ctx.setSendZuulResponse(false); throw new RateLimitExceededException(); } diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/DefaultRateLimitUtils.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/DefaultRateLimitUtils.java index 818414ad..28618d68 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/DefaultRateLimitUtils.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/DefaultRateLimitUtils.java @@ -30,6 +30,7 @@ public class DefaultRateLimitUtils implements RateLimitUtils { private static final String ANONYMOUS_USER = "anonymous"; + private static final String X_FORWARDED_FOR_HEADER_DELIMITER = ","; private final RateLimitProperties properties; @@ -46,7 +47,7 @@ public String getUser(final HttpServletRequest request) { public String getRemoteAddress(final HttpServletRequest request) { String xForwardedFor = request.getHeader(X_FORWARDED_FOR_HEADER); if (properties.isBehindProxy() && xForwardedFor != null) { - return xForwardedFor.split(",")[0].trim(); + return xForwardedFor.split(X_FORWARDED_FOR_HEADER_DELIMITER)[0].trim(); } return request.getRemoteAddr(); } diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitConstants.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitConstants.java index ab627b22..db43fa1c 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitConstants.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/RateLimitConstants.java @@ -32,5 +32,8 @@ private RateLimitConstants() { public static final String HEADER_REMAINING = "X-RateLimit-Remaining-"; public static final String HEADER_RESET = "X-RateLimit-Reset-"; public static final String REQUEST_START_TIME = "rateLimitRequestStartTime"; + public static final String CURRENT_REQUEST_ROUTE = "rateLimitRequestRoute"; + public static final String CURRENT_REQUEST_POLICY = "rateLimitRequestPolicy"; + public static final String RATE_LIMIT_EXCEEDED = "rateLimitExceeded"; } diff --git a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/StringToMatchTypeConverter.java b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/StringToMatchTypeConverter.java index 060b6ca1..311efdbd 100644 --- a/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/StringToMatchTypeConverter.java +++ b/spring-cloud-zuul-ratelimit-core/src/main/java/com/marcosbarbero/cloud/autoconfigure/zuul/ratelimit/support/StringToMatchTypeConverter.java @@ -24,7 +24,7 @@ /** * @author Liel Chayoun */ -public class StringToMatchTypeConverter implements Converter { +public final class StringToMatchTypeConverter implements Converter { private static final String DELIMITER = "=";