change escape way into encode (#251)

pull/264/head
cheese8 2 years ago committed by GitHub
parent c1b31a73e2
commit 7e672d175a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,6 +18,9 @@
package com.tencent.cloud.polaris.router.feign; 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.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -27,7 +30,6 @@ import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand; import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.ExpressionLabelUtils;
import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext; import com.tencent.cloud.polaris.router.PolarisRouterContext;
import com.tencent.cloud.polaris.router.RouterConstants; import com.tencent.cloud.polaris.router.RouterConstants;
@ -39,7 +41,8 @@ import org.springframework.util.CollectionUtils;
/** /**
* In order to pass router context for {@link com.tencent.cloud.polaris.router.PolarisLoadBalancerCompositeRule}. * In order to pass router context for {@link com.tencent.cloud.polaris.router.PolarisLoadBalancerCompositeRule}.
* *
*@author lepdou 2022-05-16 * @author lepdou 2022-05-16
* @author cheese8 2022-06-18
*/ */
public class PolarisFeignLoadBalancer extends FeignLoadBalancer { public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
@ -70,18 +73,15 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, MetadataContextHolder.get() routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, MetadataContextHolder.get()
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)); .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE));
labelHeaderValues.forEach(labelHeaderValue -> { Map<String, String> labelHeaderValuesMap = new HashMap<>();
Map<String, String> labels = JacksonUtils.deserialize2Map(labelHeaderValue); try {
if (!CollectionUtils.isEmpty(labels)) { String labelHeaderValuesContent = labelHeaderValues.stream().findFirst().get();
Map<String, String> unescapeLabels = new HashMap<>(labels.size()); labelHeaderValuesMap.putAll(JacksonUtils.deserialize2Map(URLDecoder.decode(labelHeaderValuesContent, StandardCharsets.UTF_8.name())));
for (Map.Entry<String, String> entry : labels.entrySet()) { }
String escapedKey = ExpressionLabelUtils.unescape(entry.getKey()); catch (UnsupportedEncodingException e) {
String escapedValue = ExpressionLabelUtils.unescape(entry.getValue()); throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name());
unescapeLabels.put(escapedKey, escapedValue); }
} routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labelHeaderValuesMap);
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, unescapeLabels);
}
});
return routerContext; return routerContext;
} }

@ -18,6 +18,9 @@
package com.tencent.cloud.polaris.router.feign; 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.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; 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.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; 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.common.util.JacksonUtils;
import com.tencent.cloud.polaris.router.RouterConstants; import com.tencent.cloud.polaris.router.RouterConstants;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
@ -44,7 +46,8 @@ import org.springframework.util.CollectionUtils;
/** /**
* Resolver labels from request. * Resolver labels from request.
* *
*@author lepdou 2022-05-12 * @author lepdou 2022-05-12
* @author cheese8 2022-06-18
*/ */
public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered { public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered {
private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelFeignInterceptor.class); private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelFeignInterceptor.class);
@ -102,22 +105,20 @@ public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
labels.putAll(transitiveLabels); 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<String, String> escapeLabels = new HashMap<>(labels.size());
for (Map.Entry<String, String> entry : labels.entrySet()) {
String escapedKey = ExpressionLabelUtils.escape(entry.getKey());
String escapedValue = ExpressionLabelUtils.escape(entry.getValue());
escapeLabels.put(escapedKey, escapedValue);
}
// pass label by header // pass label by header
if (escapeLabels.size() == 0) { if (labels.size() == 0) {
requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER); requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER);
return; 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<String, String> getRuleExpressionLabels(RequestTemplate requestTemplate, String peerService) { private Map<String, String> getRuleExpressionLabels(RequestTemplate requestTemplate, String peerService) {

@ -18,6 +18,9 @@
package com.tencent.cloud.polaris.router.feign; 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.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; 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.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils; 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.common.util.JacksonUtils;
import com.tencent.cloud.polaris.router.RouterConstants; import com.tencent.cloud.polaris.router.RouterConstants;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
@ -50,6 +52,7 @@ import static org.mockito.Mockito.when;
/** /**
* test for {@link RouterLabelFeignInterceptor} * test for {@link RouterLabelFeignInterceptor}
* @author lepdou 2022-05-26 * @author lepdou 2022-05-26
* @author cheese8 2022-06-18
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class RouterLabelFeignInterceptorTest { public class RouterLabelFeignInterceptorTest {
@ -116,27 +119,24 @@ public class RouterLabelFeignInterceptorTest {
routerLabelFeignInterceptor.apply(requestTemplate); routerLabelFeignInterceptor.apply(requestTemplate);
Collection<String> routerLabels = requestTemplate.headers().get(RouterConstants.ROUTER_LABEL_HEADER); Collection<String> routerLabels = requestTemplate.headers().get(RouterConstants.ROUTER_LABEL_HEADER);
Map<String, String> routerLabelsMap = new HashMap<>();
Assert.assertNotNull(routerLabels); try {
for (String value : routerLabels) { String routerLabelContent = routerLabels.stream().findFirst().get();
Map<String, String> labels = unescape(JacksonUtils.deserialize2Map(value)); routerLabelsMap.putAll(JacksonUtils.deserialize2Map(URLDecoder.decode(routerLabelContent, StandardCharsets.UTF_8.name())));
}
Assert.assertEquals("v1", labels.get("k1")); catch (UnsupportedEncodingException e) {
Assert.assertEquals("v22", labels.get("k2")); throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name());
Assert.assertEquals("v3", labels.get("k3")); }
Assert.assertEquals("v4", labels.get("k4")); Assert.assertNotNull(routerLabelsMap);
Assert.assertEquals(headerUidValue, labels.get("${http.header.uid}")); for (String value : routerLabelsMap.values()) {
Assert.assertEquals("", labels.get("${http.header.name}")); 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<String, String> unescape(Map<String, String> labels) {
Map<String, String> result = new HashMap<>();
for (Map.Entry<String, String> entry : labels.entrySet()) {
result.put(ExpressionLabelUtils.unescape(entry.getKey()), ExpressionLabelUtils.unescape(entry.getValue()));
}
return result;
}
} }

@ -40,7 +40,8 @@ import org.springframework.web.server.ServerWebExchange;
/** /**
* the utils for parse label expression. * the utils for parse label expression.
* *
*@author lepdou 2022-05-13 * @author lepdou 2022-05-13
* @author cheese8 2022-06-20
*/ */
public class ExpressionLabelUtils { public class ExpressionLabelUtils {
@ -76,18 +77,10 @@ public class ExpressionLabelUtils {
* the expression of uri. * the expression of uri.
*/ */
public static final String LABEL_URI = "${http.uri}"; public static final String LABEL_URI = "${http.uri}";
/**
* the prefix of expression.
*/
public static final String LABEL_PREFIX = "${";
/** /**
* the suffix of expression. * the suffix of expression.
*/ */
public static final String LABEL_SUFFIX = "}"; public static final String LABEL_SUFFIX = "}";
/**
* the escape prefix of label.
*/
public static final String LABEL_ESCAPE_PREFIX = "##@$@##";
public static boolean isExpressionLabel(String labelKey) { public static boolean isExpressionLabel(String labelKey) {
if (StringUtils.isEmpty(labelKey)) { if (StringUtils.isEmpty(labelKey)) {
@ -103,14 +96,6 @@ public class ExpressionLabelUtils {
&& StringUtils.endsWith(labelKey, LABEL_SUFFIX); && 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<String, String> resolve(HttpServletRequest request, Set<String> labelKeys) { public static Map<String, String> resolve(HttpServletRequest request, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) { if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap(); return Collections.emptyMap();

@ -38,7 +38,8 @@ import org.springframework.mock.web.server.MockServerWebExchange;
/** /**
* test for {@link ExpressionLabelUtils} * test for {@link ExpressionLabelUtils}
*@author lepdou 2022-05-27 * @author lepdou 2022-05-27
* @auther cheese8 2022-06-20
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ExpressionLabelUtilsTest { public class ExpressionLabelUtilsTest {
@ -76,39 +77,6 @@ public class ExpressionLabelUtilsTest {
Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel9)); 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 @Test
public void testResolveHttpServletRequest() { public void testResolveHttpServletRequest() {
String validLabel1 = "${http.query.uid}"; String validLabel1 = "${http.query.uid}";

Loading…
Cancel
Save