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/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}";