diff --git a/.github/workflows/junit_test.yml b/.github/workflows/junit_test.yml index f2693c44f..51e9cce15 100644 --- a/.github/workflows/junit_test.yml +++ b/.github/workflows/junit_test.yml @@ -5,24 +5,47 @@ name: Test with Junit on: push: - branches: [ 2020.0 ] + branches: + - main + - 2021.0 + - 2020.0 + - greenwich pull_request: - branches: [ 2020.0 ] + branches: + - main + - 2021.0 + - 2020.0 + - greenwich jobs: build: + strategy: + matrix: + java: [ 8, 11, 17 ] + os: [ 'windows-latest', 'macos-latest', 'ubuntu-latest' ] - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - name: Checkout codes - uses: actions/checkout@v2 - - name: Set up JDK 8 - uses: actions/setup-java@v2 + uses: actions/checkout@v3 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 with: - java-version: '8' - distribution: 'adopt' + distribution: 'temurin' + java-version: ${{ matrix.java }} + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- # - name: Build with Maven # run: mvn -B package --file pom.xml - name: Test with Maven run: mvn -B test --file pom.xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: '**/target/site/jacoco/jacoco.xml' diff --git a/CHANGELOG.md b/CHANGELOG.md index 874b31279..cd1e5d177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,7 @@ - [Feature: Add config module unit test](https://github.com/Tencent/spring-cloud-tencent/pull/255) - [Upgrade: fix third-party lib CVEs & upgrade core spring libs version](https://github.com/Tencent/spring-cloud-tencent/pull/258) - [feat:support reading configuration from application.yml or application.properties.](https://github.com/Tencent/spring-cloud-tencent/pull/261) +- [change the way from escape to encode in 2020.0](https://github.com/Tencent/spring-cloud-tencent/pull/257) +- [fix:fix ClassNotFoundException while not importing openfeign when using circuit-breaker module.](https://github.com/Tencent/spring-cloud-tencent/pull/270) +- [fix:solve the chaos code problem on rejectTips](https://github.com/Tencent/spring-cloud-tencent/pull/283) +- [fix:solve ratelimit-callee-service UnknownHostException.](https://github.com/Tencent/spring-cloud-tencent/pull/291) diff --git a/pom.xml b/pom.xml index a20e5fdf4..ff568384d 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ 5.3.21 - 0.8.3 + 0.8.8 3.2.0 1.2.7 3.0.1 diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java index bcaaf3ea9..ae699dead 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java @@ -25,6 +25,7 @@ import com.tencent.polaris.factory.api.DiscoveryAPIFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.context.annotation.Bean; @@ -41,6 +42,7 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; */ @ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true) @Configuration(proxyBeanMethods = false) +@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration") @AutoConfigureAfter(PolarisContextAutoConfiguration.class) @AutoConfigureBefore(FeignAutoConfiguration.class) public class PolarisFeignClientAutoConfiguration { diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java index f3f70b4bc..c408f4f54 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java @@ -66,7 +66,7 @@ public class ConfigChangeListenerTest { Sets.newHashSet("timeout")); applicationEventPublisher.publishEvent(event); - + Thread.sleep(200); //after change Assert.assertEquals(2, testConfig.getChangeCnt()); Assert.assertEquals(2000, testConfig.getTimeout()); diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java index 7340c73de..8796fc1ab 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java @@ -55,7 +55,7 @@ import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LAB /** * Reactive filter to check quota. * - * @author Haotian Zhang, lepdou + * @author Haotian Zhang, lepdou, cheese8 */ public class QuotaCheckReactiveFilter implements WebFilter, Ordered { @@ -106,7 +106,7 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered { if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { ServerHttpResponse response = exchange.getResponse(); response.setRawStatusCode(polarisRateLimitProperties.getRejectHttpCode()); - response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + response.getHeaders().setContentType(MediaType.TEXT_HTML); DataBuffer dataBuffer = response.bufferFactory().allocateBuffer() .write(rejectTips.getBytes(StandardCharsets.UTF_8)); return response.writeWith(Mono.just(dataBuffer)); diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java index 08767b721..f3e4c3b07 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java @@ -53,7 +53,7 @@ import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LAB /** * Servlet filter to check quota. * - * @author Haotian Zhang, lepdou + * @author Haotian Zhang, lepdou, cheese8 */ @Order(RateLimitConstant.FILTER_ORDER) public class QuotaCheckServletFilter extends OncePerRequestFilter { @@ -99,6 +99,7 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter { if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { response.setStatus(polarisRateLimitProperties.getRejectHttpCode()); + response.setContentType("text/html;charset=UTF-8"); response.getWriter().write(rejectTips); return; } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java index 3bae2e3bd..0a9a4988f 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java @@ -65,7 +65,7 @@ import static org.mockito.Mockito.when; /** * Test for {@link QuotaCheckReactiveFilter}. * - * @author Haotian Zhang + * @author Haotian Zhang, cheese8 */ @RunWith(MockitoJUnitRunner.class) @SpringBootTest(classes = QuotaCheckReactiveFilterTest.TestApplication.class, properties = { @@ -118,7 +118,7 @@ public class QuotaCheckReactiveFilterTest { }); PolarisRateLimitProperties polarisRateLimitProperties = new PolarisRateLimitProperties(); - polarisRateLimitProperties.setRejectRequestTips("RejectRequestTips"); + polarisRateLimitProperties.setRejectRequestTips("RejectRequestTips提示消息"); polarisRateLimitProperties.setRejectHttpCode(419); RateLimitRuleLabelResolver rateLimitRuleLabelResolver = mock(RateLimitRuleLabelResolver.class); @@ -138,7 +138,7 @@ public class QuotaCheckReactiveFilterTest { try { Field rejectTips = QuotaCheckReactiveFilter.class.getDeclaredField("rejectTips"); rejectTips.setAccessible(true); - assertThat(rejectTips.get(quotaCheckReactiveFilter)).isEqualTo("RejectRequestTips"); + assertThat(rejectTips.get(quotaCheckReactiveFilter)).isEqualTo("RejectRequestTips提示消息"); } catch (NoSuchFieldException | IllegalAccessException e) { fail("Exception encountered.", e); diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java index a2cc344dc..3f93f0a3b 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java @@ -65,7 +65,7 @@ import static org.mockito.Mockito.when; /** * Test for {@link QuotaCheckServletFilter}. * - * @author Haotian Zhang + * @author Haotian Zhang, cheese8 */ @RunWith(MockitoJUnitRunner.class) @SpringBootTest(classes = QuotaCheckServletFilterTest.TestApplication.class, properties = { @@ -77,6 +77,8 @@ public class QuotaCheckServletFilterTest { private QuotaCheckServletFilter quotaCheckServletFilter; + private QuotaCheckServletFilter quotaCheckWithHtmlRejectTipsServletFilter; + private static MockedStatic mockedApplicationContextAwareUtils; private static MockedStatic expressionLabelUtilsMockedStatic; @BeforeClass @@ -87,7 +89,6 @@ public class QuotaCheckServletFilterTest { mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) .thenReturn("unit-test"); - } @AfterClass @@ -118,13 +119,18 @@ public class QuotaCheckServletFilterTest { }); PolarisRateLimitProperties polarisRateLimitProperties = new PolarisRateLimitProperties(); - polarisRateLimitProperties.setRejectRequestTips("RejectRequestTips"); + polarisRateLimitProperties.setRejectRequestTips("RejectRequestTips提示消息"); polarisRateLimitProperties.setRejectHttpCode(419); + PolarisRateLimitProperties polarisRateLimitWithHtmlRejectTipsProperties = new PolarisRateLimitProperties(); + polarisRateLimitWithHtmlRejectTipsProperties.setRejectRequestTips("

RejectRequestTips提示消息

"); + polarisRateLimitWithHtmlRejectTipsProperties.setRejectHttpCode(419); + RateLimitRuleLabelResolver rateLimitRuleLabelResolver = mock(RateLimitRuleLabelResolver.class); when(rateLimitRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString())).thenReturn(Collections.EMPTY_SET); this.quotaCheckServletFilter = new QuotaCheckServletFilter(limitAPI, labelResolver, polarisRateLimitProperties, rateLimitRuleLabelResolver); + this.quotaCheckWithHtmlRejectTipsServletFilter = new QuotaCheckServletFilter(limitAPI, labelResolver, polarisRateLimitWithHtmlRejectTipsProperties, rateLimitRuleLabelResolver); } @Test @@ -133,7 +139,16 @@ public class QuotaCheckServletFilterTest { try { Field rejectTips = QuotaCheckServletFilter.class.getDeclaredField("rejectTips"); rejectTips.setAccessible(true); - assertThat(rejectTips.get(quotaCheckServletFilter)).isEqualTo("RejectRequestTips"); + assertThat(rejectTips.get(quotaCheckServletFilter)).isEqualTo("RejectRequestTips提示消息"); + } + catch (NoSuchFieldException | IllegalAccessException e) { + fail("Exception encountered.", e); + } + quotaCheckWithHtmlRejectTipsServletFilter.init(); + try { + Field rejectTips = QuotaCheckServletFilter.class.getDeclaredField("rejectTips"); + rejectTips.setAccessible(true); + assertThat(rejectTips.get(quotaCheckWithHtmlRejectTipsServletFilter)).isEqualTo("

RejectRequestTips提示消息

"); } catch (NoSuchFieldException | IllegalAccessException e) { fail("Exception encountered.", e); @@ -201,8 +216,11 @@ public class QuotaCheckServletFilterTest { MetadataContext.LOCAL_SERVICE = "TestApp3"; quotaCheckServletFilter.doFilterInternal(request, response, filterChain); assertThat(response.getStatus()).isEqualTo(419); - assertThat(response.getContentAsString()).isEqualTo("RejectRequestTips"); + assertThat(response.getContentAsString()).isEqualTo("RejectRequestTips提示消息"); + quotaCheckWithHtmlRejectTipsServletFilter.doFilterInternal(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(419); + assertThat(response.getContentAsString()).isEqualTo("RejectRequestTips提示消息"); // Exception MetadataContext.LOCAL_SERVICE = "TestApp4"; diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterServiceInstanceListSupplier.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterServiceInstanceListSupplier.java index 13b4afc17..d2f5e2f4e 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterServiceInstanceListSupplier.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterServiceInstanceListSupplier.java @@ -17,6 +17,9 @@ package com.tencent.cloud.polaris.router; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -27,7 +30,6 @@ import java.util.Map; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.pojo.PolarisServiceInstance; -import com.tencent.cloud.common.util.ExpressionLabelUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.polaris.loadbalancer.LoadBalancerUtils; import com.tencent.cloud.polaris.router.config.PolarisMetadataRouterProperties; @@ -66,7 +68,7 @@ import org.springframework.util.CollectionUtils; * And {@link PolarisRouterServiceInstanceListSupplier#get(Request)} provides the ability to pass in http headers, * so routing capabilities are implemented through IRule. * - * @author Haotian Zhang, lepdou + * @author Haotian Zhang, lepdou, cheese8 */ public class PolarisRouterServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier { @@ -123,19 +125,15 @@ public class PolarisRouterServiceInstanceListSupplier extends DelegatingServiceI routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, MetadataContextHolder.get() .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)); - labelHeaderValues.forEach(labelHeaderValue -> { - Map labels = JacksonUtils.deserialize2Map(labelHeaderValue); - if (!CollectionUtils.isEmpty(labels)) { - Map unescapeLabels = new HashMap<>(labels.size()); - for (Map.Entry entry : labels.entrySet()) { - String escapedKey = ExpressionLabelUtils.unescape(entry.getKey()); - String escapedValue = ExpressionLabelUtils.unescape(entry.getValue()); - unescapeLabels.put(escapedKey, escapedValue); - } - routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, unescapeLabels); - } - }); - + Map labelHeaderValuesMap = new HashMap<>(); + try { + String labelHeaderValuesContent = labelHeaderValues.stream().findFirst().get(); + labelHeaderValuesMap.putAll(JacksonUtils.deserialize2Map(URLDecoder.decode(labelHeaderValuesContent, StandardCharsets.UTF_8.name()))); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name()); + } + routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labelHeaderValuesMap); return routerContext; } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java index fac0879b4..c2f339b02 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java @@ -18,6 +18,9 @@ package com.tencent.cloud.polaris.router.feign; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -28,7 +31,6 @@ import java.util.Set; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; -import com.tencent.cloud.common.util.ExpressionLabelUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.polaris.router.RouterConstants; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; @@ -44,7 +46,8 @@ import org.springframework.util.CollectionUtils; /** * Resolver labels from request. * - *@author lepdou 2022-05-12 + * @author lepdou 2022-05-12 + * @author cheese8 2022-06-20 */ public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered { private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelFeignInterceptor.class); @@ -102,22 +105,20 @@ public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); labels.putAll(transitiveLabels); - // Because when the label is placed in RequestTemplate.header, - // RequestTemplate will parse the header according to the regular, which conflicts with the expression. - // Avoid conflicts by escaping. - Map escapeLabels = new HashMap<>(labels.size()); - for (Map.Entry entry : labels.entrySet()) { - String escapedKey = ExpressionLabelUtils.escape(entry.getKey()); - String escapedValue = ExpressionLabelUtils.escape(entry.getValue()); - escapeLabels.put(escapedKey, escapedValue); - } - // pass label by header - if (escapeLabels.size() == 0) { + if (labels.size() == 0) { requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER); return; } - requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER, JacksonUtils.serialize2Json(escapeLabels)); + + String encodedLabelsContent; + try { + encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), StandardCharsets.UTF_8.name()); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name()); + } + requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER, encodedLabelsContent); } private Map getRuleExpressionLabels(RequestTemplate requestTemplate, String peerService) { diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java index eb7c90ce0..2006a373f 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java @@ -19,7 +19,10 @@ package com.tencent.cloud.polaris.router.resttemplate; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -52,7 +55,8 @@ import org.springframework.util.CollectionUtils; * PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor capabilities. * Parses the label from the request and puts it into the RouterContext for routing. * - *@author lepdou 2022-05-18 + * @author lepdou 2022-05-18 + * @author cheese8 2022-06-20 */ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancerInterceptor.class); @@ -126,22 +130,19 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor { .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); labels.putAll(transitiveLabels); - // Because when the label is placed in RequestTemplate.header, - // RequestTemplate will parse the header according to the regular, which conflicts with the expression. - // Avoid conflicts by escaping. - Map escapeLabels = new HashMap<>(labels.size()); - for (Map.Entry entry : labels.entrySet()) { - String escapedKey = ExpressionLabelUtils.escape(entry.getKey()); - String escapedValue = ExpressionLabelUtils.escape(entry.getValue()); - escapeLabels.put(escapedKey, escapedValue); - } - // pass label by header - if (escapeLabels.size() == 0) { + if (labels.size() == 0) { request.getHeaders().set(RouterConstants.ROUTER_LABEL_HEADER, null); return; } - request.getHeaders().set(RouterConstants.ROUTER_LABEL_HEADER, JacksonUtils.serialize2Json(escapeLabels)); + String encodedLabelsContent; + try { + encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), StandardCharsets.UTF_8.name()); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name()); + } + request.getHeaders().set(RouterConstants.ROUTER_LABEL_HEADER, encodedLabelsContent); } private Map getExpressionLabels(HttpRequest request, String peerServiceName) { diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java index d5f7076fc..b2824b7ea 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java @@ -18,6 +18,9 @@ package com.tencent.cloud.polaris.router.feign; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -29,7 +32,6 @@ import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; -import com.tencent.cloud.common.util.ExpressionLabelUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.polaris.router.RouterConstants; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; @@ -50,6 +52,7 @@ import static org.mockito.Mockito.when; /** * test for {@link RouterLabelFeignInterceptor} * @author lepdou 2022-05-26 + * @author cheese8 2022-06-20 */ @RunWith(MockitoJUnitRunner.class) public class RouterLabelFeignInterceptorTest { @@ -117,26 +120,24 @@ public class RouterLabelFeignInterceptorTest { Collection routerLabels = requestTemplate.headers().get(RouterConstants.ROUTER_LABEL_HEADER); - Assert.assertNotNull(routerLabels); - for (String value : routerLabels) { - Map labels = unescape(JacksonUtils.deserialize2Map(value)); - - Assert.assertEquals("v1", labels.get("k1")); - Assert.assertEquals("v22", labels.get("k2")); - Assert.assertEquals("v3", labels.get("k3")); - Assert.assertEquals("v4", labels.get("k4")); - Assert.assertEquals(headerUidValue, labels.get("${http.header.uid}")); - Assert.assertEquals("", labels.get("${http.header.name}")); + Map routerLabelsMap = new HashMap<>(); + try { + String routerLabelContent = routerLabels.stream().findFirst().get(); + routerLabelsMap.putAll(JacksonUtils.deserialize2Map(URLDecoder.decode(routerLabelContent, StandardCharsets.UTF_8.name()))); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name()); } + + Assert.assertNotNull(routerLabels); + Assert.assertEquals("v1", routerLabelsMap.get("k1")); + Assert.assertEquals("v22", routerLabelsMap.get("k2")); + Assert.assertEquals("v3", routerLabelsMap.get("k3")); + Assert.assertEquals("v4", routerLabelsMap.get("k4")); + Assert.assertEquals(headerUidValue, routerLabelsMap.get("${http.header.uid}")); + Assert.assertEquals("", routerLabelsMap.get("${http.header.name}")); } } } - private Map unescape(Map labels) { - Map result = new HashMap<>(); - for (Map.Entry entry : labels.entrySet()) { - result.put(ExpressionLabelUtils.unescape(entry.getKey()), ExpressionLabelUtils.unescape(entry.getValue())); - } - return result; - } } diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java index 5162d8a8d..11319f650 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java @@ -18,8 +18,9 @@ package com.tencent.cloud.polaris.router.resttemplate; - import java.net.URI; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -60,6 +61,7 @@ import static org.mockito.Mockito.when; /** * test for {@link PolarisLoadBalancerInterceptor} * @author lepdou 2022-05-26 + * @author cheese8 2022-06-20 */ @RunWith(MockitoJUnitRunner.class) public class PolarisLoadBalancerInterceptorTest { @@ -184,13 +186,13 @@ public class PolarisLoadBalancerInterceptorTest { verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService); verify(routerLabelResolver).resolve(request, null); - Map headers = JacksonUtils.deserialize2Map(request.getHeaders() - .get(RouterConstants.ROUTER_LABEL_HEADER).get(0)); + Map headers = JacksonUtils.deserialize2Map(URLDecoder.decode(request.getHeaders() + .get(RouterConstants.ROUTER_LABEL_HEADER).get(0), StandardCharsets.UTF_8.name())); Assert.assertEquals("v1", headers.get("k1")); Assert.assertEquals("v22", headers.get("k2")); Assert.assertEquals("v4", headers.get("k4")); - Assert.assertEquals("GET", headers.get("##@$@##http.method}")); - Assert.assertEquals("/user/get", headers.get("##@$@##http.uri}")); + Assert.assertEquals("GET", headers.get("${http.method}")); + Assert.assertEquals("/user/get", headers.get("${http.uri}")); } static class MockedLoadBalancerRequest implements LoadBalancerRequest { diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ExpressionLabelUtils.java index 41d70abd7..a4c53ccc1 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ExpressionLabelUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ExpressionLabelUtils.java @@ -40,7 +40,8 @@ import org.springframework.web.server.ServerWebExchange; /** * the utils for parse label expression. * - *@author lepdou 2022-05-13 + * @author lepdou 2022-05-13 + * @author cheese8 2022-06-20 */ public class ExpressionLabelUtils { @@ -76,18 +77,10 @@ public class ExpressionLabelUtils { * the expression of uri. */ public static final String LABEL_URI = "${http.uri}"; - /** - * the prefix of expression. - */ - public static final String LABEL_PREFIX = "${"; /** * the suffix of expression. */ public static final String LABEL_SUFFIX = "}"; - /** - * the escape prefix of label. - */ - public static final String LABEL_ESCAPE_PREFIX = "##@$@##"; public static boolean isExpressionLabel(String labelKey) { if (StringUtils.isEmpty(labelKey)) { @@ -103,14 +96,6 @@ public class ExpressionLabelUtils { && StringUtils.endsWith(labelKey, LABEL_SUFFIX); } - public static String escape(String str) { - return StringUtils.replace(str, LABEL_PREFIX, LABEL_ESCAPE_PREFIX); - } - - public static String unescape(String str) { - return StringUtils.replace(str, LABEL_ESCAPE_PREFIX, LABEL_PREFIX); - } - public static Map resolve(HttpServletRequest request, Set labelKeys) { if (CollectionUtils.isEmpty(labelKeys)) { return Collections.emptyMap(); diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java index d79bfce8b..b5ea45d5d 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.springframework.core.io.ClassPathResource; +import org.springframework.util.StreamUtils; /** * Read file content from classpath resource. @@ -35,20 +36,15 @@ public final class ResourceFileUtils { } public static String readFile(String path) throws IOException { - StringBuilder sb = new StringBuilder(); ClassPathResource classPathResource = new ClassPathResource(path); if (classPathResource.exists() && classPathResource.isReadable()) { try (InputStream inputStream = classPathResource.getInputStream()) { - byte[] buffer = new byte[1024 * 10]; - int len; - while ((len = inputStream.read(buffer)) != -1) { - sb.append(new String(buffer, 0, len, StandardCharsets.UTF_8)); - } + return StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); } } - return sb.toString(); + return ""; } } diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ExpressionLabelUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ExpressionLabelUtilsTest.java index 3c7eabed7..46b3fa1fe 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ExpressionLabelUtilsTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ExpressionLabelUtilsTest.java @@ -38,7 +38,8 @@ import org.springframework.mock.web.server.MockServerWebExchange; /** * test for {@link ExpressionLabelUtils} - *@author lepdou 2022-05-27 + * @author lepdou 2022-05-27 + * @author cheese8 2022-06-20 */ @RunWith(MockitoJUnitRunner.class) public class ExpressionLabelUtilsTest { @@ -76,39 +77,6 @@ public class ExpressionLabelUtilsTest { Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel9)); } - @Test - public void testEscape() { - String validLabel1 = "${http.query.uid}"; - String validLabel2 = "${http.header.uid}"; - String validLabel3 = "${http.cookie.uid}"; - String validLabel4 = "${http.method}"; - String validLabel5 = "${http.uri}"; - String invalidLabel1 = "${http.queryuid}"; - String invalidLabel2 = "{http.query.uid}"; - String invalidLabel3 = "${http.query.uid"; - String invalidLabel4 = "$ {http.query.uid}"; - String invalidLabel5 = "${ http.query.uid}"; - String invalidLabel6 = "${query.uid}"; - String invalidLabel7 = "http.query.uid"; - String invalidLabel8 = "$${http.uri}"; - String invalidLabel9 = "#{http.uri}"; - - Assert.assertEquals(validLabel1, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(validLabel1))); - Assert.assertEquals(validLabel2, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(validLabel2))); - Assert.assertEquals(validLabel3, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(validLabel3))); - Assert.assertEquals(validLabel4, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(validLabel4))); - Assert.assertEquals(validLabel5, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(validLabel5))); - Assert.assertEquals(invalidLabel1, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel1))); - Assert.assertEquals(invalidLabel2, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel2))); - Assert.assertEquals(invalidLabel3, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel3))); - Assert.assertEquals(invalidLabel4, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel4))); - Assert.assertEquals(invalidLabel5, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel5))); - Assert.assertEquals(invalidLabel6, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel6))); - Assert.assertEquals(invalidLabel7, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel7))); - Assert.assertEquals(invalidLabel8, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel8))); - Assert.assertEquals(invalidLabel9, ExpressionLabelUtils.unescape(ExpressionLabelUtils.escape(invalidLabel9))); - } - @Test public void testResolveHttpServletRequest() { String validLabel1 = "${http.query.uid}"; diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ResourceFileUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ResourceFileUtilsTest.java index 334cd4e20..769d99ce7 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ResourceFileUtilsTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ResourceFileUtilsTest.java @@ -35,7 +35,7 @@ public class ResourceFileUtilsTest { @Test public void testReadExistedFile() throws IOException { String content = ResourceFileUtils.readFile("test.txt"); - Assert.assertEquals("just for test\n", content); + Assert.assertEquals("just for test", content); } @Test diff --git a/spring-cloud-tencent-commons/src/test/resources/test.txt b/spring-cloud-tencent-commons/src/test/resources/test.txt index 63d3c2d75..e18c37483 100644 --- a/spring-cloud-tencent-commons/src/test/resources/test.txt +++ b/spring-cloud-tencent-commons/src/test/resources/test.txt @@ -1 +1 @@ -just for test +just for test \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml index dd28040b5..bf89b34d6 100644 --- a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml +++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml @@ -18,6 +18,11 @@ spring-boot-starter-web + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + com.tencent.cloud spring-cloud-starter-tencent-polaris-ratelimit