diff --git a/CHANGELOG.md b/CHANGELOG.md
index 853ef1c6..1051f15d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,3 +5,4 @@
- [feat:upgrade jackson version.](https://github.com/Tencent/spring-cloud-tencent/pull/1261)
- [fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5.](https://github.com/Tencent/spring-cloud-tencent/pull/1267)
- [feat: support lossless online and offline](https://github.com/Tencent/spring-cloud-tencent/pull/1268)
+- [feat: support lane router](https://github.com/Tencent/spring-cloud-tencent/pull/1269)
diff --git a/spring-cloud-starter-tencent-metadata-transfer/pom.xml b/spring-cloud-starter-tencent-metadata-transfer/pom.xml
index 642bbec1..ca8350c5 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/pom.xml
+++ b/spring-cloud-starter-tencent-metadata-transfer/pom.xml
@@ -19,6 +19,12 @@
com.tencent.cloud
spring-cloud-tencent-commons
+
+
+ com.tencent.cloud
+ spring-cloud-tencent-rpc-enhancement
+
+
@@ -50,6 +56,24 @@
spring-cloud-starter-loadbalancer
test
+
+
+ com.tencent.polaris
+ polaris-test-mock-discovery
+ test
+
+
+
+ com.tencent.polaris
+ polaris-test-common
+ test
+
+
+
+ org.mockito
+ mockito-inline
+ test
+
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java
index 6d5cb6da..a2431755 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java
@@ -18,29 +18,21 @@
package com.tencent.cloud.metadata.config;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.metadata.core.DecodeTransferMetadataReactiveFilter;
import com.tencent.cloud.metadata.core.DecodeTransferMetadataServletFilter;
-import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor;
-import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor;
-import com.tencent.cloud.metadata.core.EncodeTransferMedataScgFilter;
-import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientFilter;
+import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignEnhancedPlugin;
+import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedPlugin;
+import com.tencent.cloud.metadata.core.EncodeTransferMedataScgEnhancedPlugin;
+import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin;
+import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
-import org.springframework.beans.factory.SmartInitializingSingleton;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
-import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.reactive.function.client.WebClient;
import static jakarta.servlet.DispatcherType.ASYNC;
import static jakarta.servlet.DispatcherType.ERROR;
@@ -74,8 +66,8 @@ public class MetadataTransferAutoConfiguration {
}
@Bean
- public DecodeTransferMetadataServletFilter metadataServletFilter() {
- return new DecodeTransferMetadataServletFilter();
+ public DecodeTransferMetadataServletFilter metadataServletFilter(PolarisContextProperties polarisContextProperties) {
+ return new DecodeTransferMetadataServletFilter(polarisContextProperties);
}
}
@@ -87,8 +79,8 @@ public class MetadataTransferAutoConfiguration {
protected static class MetadataReactiveFilterConfig {
@Bean
- public DecodeTransferMetadataReactiveFilter metadataReactiveFilter() {
- return new DecodeTransferMetadataReactiveFilter();
+ public DecodeTransferMetadataReactiveFilter metadataReactiveFilter(PolarisContextProperties polarisContextProperties) {
+ return new DecodeTransferMetadataReactiveFilter(polarisContextProperties);
}
}
@@ -97,11 +89,12 @@ public class MetadataTransferAutoConfiguration {
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.GlobalFilter")
+ @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true)
protected static class MetadataTransferScgFilterConfig {
@Bean
- public GlobalFilter encodeTransferMedataScgFilter() {
- return new EncodeTransferMedataScgFilter();
+ public EncodeTransferMedataScgEnhancedPlugin encodeTransferMedataScgEnhancedPlugin() {
+ return new EncodeTransferMedataScgEnhancedPlugin();
}
}
@@ -110,11 +103,12 @@ public class MetadataTransferAutoConfiguration {
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.Feign")
+ @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true)
protected static class MetadataTransferFeignInterceptorConfig {
@Bean
- public EncodeTransferMedataFeignInterceptor encodeTransferMedataFeignInterceptor() {
- return new EncodeTransferMedataFeignInterceptor();
+ public EncodeTransferMedataFeignEnhancedPlugin encodeTransferMedataFeignEnhancedPlugin() {
+ return new EncodeTransferMedataFeignEnhancedPlugin();
}
}
@@ -123,23 +117,12 @@ public class MetadataTransferAutoConfiguration {
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
+ @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true)
protected static class MetadataTransferRestTemplateConfig {
- @Autowired(required = false)
- private List restTemplates = Collections.emptyList();
-
- @Bean
- public EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor() {
- return new EncodeTransferMedataRestTemplateInterceptor();
- }
-
@Bean
- public SmartInitializingSingleton addEncodeTransferMetadataInterceptorForRestTemplate(EncodeTransferMedataRestTemplateInterceptor interceptor) {
- return () -> restTemplates.forEach(restTemplate -> {
- List list = new ArrayList<>(restTemplate.getInterceptors());
- list.add(interceptor);
- restTemplate.setInterceptors(list);
- });
+ public EncodeTransferMedataRestTemplateEnhancedPlugin encodeTransferMedataRestTemplateEnhancedPlugin() {
+ return new EncodeTransferMedataRestTemplateEnhancedPlugin();
}
}
@@ -148,20 +131,12 @@ public class MetadataTransferAutoConfiguration {
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
+ @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true)
protected static class MetadataTransferWebClientConfig {
- @Autowired(required = false)
- private List webClientBuilder = Collections.emptyList();
-
- @Bean
- public EncodeTransferMedataWebClientFilter encodeTransferMedataWebClientFilter() {
- return new EncodeTransferMedataWebClientFilter();
- }
@Bean
- public SmartInitializingSingleton addEncodeTransferMetadataFilterForWebClient(EncodeTransferMedataWebClientFilter filter) {
- return () -> webClientBuilder.forEach(webClient -> {
- webClient.filter(filter);
- });
+ public EncodeTransferMedataWebClientEnhancedPlugin encodeTransferMedataWebClientEnhancedPlugin() {
+ return new EncodeTransferMedataWebClientEnhancedPlugin();
}
}
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java
index a1bf7a86..44a8e881 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java
@@ -18,8 +18,6 @@
package com.tencent.cloud.metadata.core;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
@@ -27,6 +25,9 @@ import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.metadata.provider.ReactiveMetadataProvider;
+import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
@@ -34,12 +35,10 @@ import reactor.core.publisher.Mono;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@@ -51,8 +50,14 @@ import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUST
*/
public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered {
+ private PolarisContextProperties polarisContextProperties;
+
private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataReactiveFilter.class);
+ public DecodeTransferMetadataReactiveFilter(PolarisContextProperties polarisContextProperties) {
+ this.polarisContextProperties = polarisContextProperties;
+ }
+
@Override
public int getOrder() {
return OrderConstant.Server.Reactive.DECODE_TRANSFER_METADATA_FILTER_ORDER;
@@ -62,19 +67,16 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
// Get metadata string from http header.
ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest();
-
Map internalTransitiveMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_METADATA);
Map customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange);
Map mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
-
Map internalDisposableMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_DISPOSABLE_METADATA);
Map mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
-
- MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata);
-
+ ReactiveMetadataProvider metadataProvider = new ReactiveMetadataProvider(serverHttpRequest, polarisContextProperties.getLocalIpAddress());
+ MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider);
// Save to ServerWebExchange.
serverWebExchange.getAttributes().put(
MetadataConstant.HeaderName.METADATA_CONTEXT,
@@ -82,20 +84,14 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
TransHeadersTransfer.transfer(serverHttpRequest);
return webFilterChain.filter(serverWebExchange)
+ .doOnError(throwable -> LOG.error("handle metadata[{}] error.",
+ MetadataContextHolder.get(), throwable))
.doFinally((type) -> MetadataContextHolder.remove());
}
private Map getIntervalMetadata(ServerHttpRequest serverHttpRequest, String headerName) {
HttpHeaders httpHeaders = serverHttpRequest.getHeaders();
- String customMetadataStr = httpHeaders.getFirst(headerName);
- try {
- if (StringUtils.hasText(customMetadataStr)) {
- customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8);
- }
- }
- catch (UnsupportedEncodingException e) {
- LOG.error("Runtime system does not support utf-8 coding.", e);
- }
+ String customMetadataStr = UrlUtils.decode(httpHeaders.getFirst(headerName));
LOG.debug("Get upstream metadata string: {}", customMetadataStr);
return JacksonUtils.deserialize2Map(customMetadataStr);
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java
index ac6a2166..8d241acc 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java
@@ -19,14 +19,15 @@
package com.tencent.cloud.metadata.core;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.metadata.provider.ServletMetadataProvider;
+import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@@ -36,10 +37,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.lang.NonNull;
-import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@@ -54,6 +53,12 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataServletFilter.class);
+ private PolarisContextProperties polarisContextProperties;
+
+ public DecodeTransferMetadataServletFilter(PolarisContextProperties polarisContextProperties) {
+ this.polarisContextProperties = polarisContextProperties;
+ }
+
@Override
protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest,
@NonNull HttpServletResponse httpServletResponse, FilterChain filterChain)
@@ -64,11 +69,10 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
Map mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
-
Map internalDisposableMetadata = getInternalMetadata(httpServletRequest, CUSTOM_DISPOSABLE_METADATA);
Map mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
-
- MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata);
+ ServletMetadataProvider metadataProvider = new ServletMetadataProvider(httpServletRequest, polarisContextProperties.getLocalIpAddress());
+ MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider);
TransHeadersTransfer.transfer(httpServletRequest);
try {
@@ -82,15 +86,7 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
private Map getInternalMetadata(HttpServletRequest httpServletRequest, String headerName) {
// Get custom metadata string from http header.
- String customMetadataStr = httpServletRequest.getHeader(headerName);
- try {
- if (StringUtils.hasText(customMetadataStr)) {
- customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8);
- }
- }
- catch (UnsupportedEncodingException e) {
- LOG.error("Runtime system does not support utf-8 coding.", e);
- }
+ String customMetadataStr = UrlUtils.decode(httpServletRequest.getHeader(headerName));
LOG.debug("Get upstream metadata string: {}", customMetadataStr);
// create custom metadata.
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignEnhancedPlugin.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignEnhancedPlugin.java
new file mode 100644
index 00000000..ff722edc
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignEnhancedPlugin.java
@@ -0,0 +1,140 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ *
+ */
+
+package com.tencent.cloud.metadata.core;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.common.util.ReflectionUtils;
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType;
+import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataType;
+import feign.Request;
+
+import org.springframework.util.CollectionUtils;
+
+import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
+import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
+
+/**
+ * Pre EnhancedPlugin for feign to encode transfer metadata.
+ *
+ * @author Shedfree Wu
+ */
+public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin {
+ @Override
+ public EnhancedPluginType getType() {
+ return EnhancedPluginType.Client.PRE;
+ }
+
+ @Override
+ public void run(EnhancedPluginContext context) throws Throwable {
+ if (!(context.getOriginRequest() instanceof Request)) {
+ return;
+ }
+ Request request = (Request) context.getOriginRequest();
+
+ // get metadata of current thread
+ MetadataContext metadataContext = MetadataContextHolder.get();
+ Map customMetadata = metadataContext.getCustomMetadata();
+ Map disposableMetadata = metadataContext.getDisposableMetadata();
+ Map transHeaders = metadataContext.getTransHeadersKV();
+
+ MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
+ Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
+ // currently only support transitive header from calleeMessageMetadataContainer
+ this.buildHeaderMap(request, calleeTransitiveHeaders);
+
+ // build custom disposable metadata request header
+ this.buildMetadataHeader(request, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
+
+ // process custom metadata
+ this.buildMetadataHeader(request, customMetadata, CUSTOM_METADATA);
+
+ // set headers that need to be transmitted from the upstream
+ this.buildTransmittedHeader(request, transHeaders);
+ }
+
+ private void buildTransmittedHeader(Request request, Map transHeaders) {
+ if (!CollectionUtils.isEmpty(transHeaders)) {
+ Map> headers = getModifiableHeaders(request);
+ transHeaders.entrySet().stream().forEach(entry -> {
+ headers.remove(entry.getKey());
+ headers.put(entry.getKey(), Arrays.asList(entry.getValue()));
+ });
+ }
+ }
+
+ /**
+ * Set metadata into the request header for {@link Request} .
+ * @param request instance of {@link Request}
+ * @param metadata metadata map .
+ * @param headerName target metadata http header name .
+ */
+ private void buildMetadataHeader(Request request, Map metadata, String headerName) {
+ if (!CollectionUtils.isEmpty(metadata)) {
+ buildHeaderMap(request, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata)));
+ }
+ }
+
+
+ /**
+ * Set headerMap into the request header for {@link Request} .
+ * @param request instance of {@link Request}
+ * @param headerMap header map .
+ */
+ private void buildHeaderMap(Request request, Map headerMap) {
+ if (!CollectionUtils.isEmpty(headerMap)) {
+ Map> headers = getModifiableHeaders(request);
+ headerMap.forEach((key, value) -> headers.put(key, Arrays.asList(UrlUtils.encode(value))));
+ }
+ }
+
+ /**
+ * The value obtained directly from the headers method is an unmodifiable map.
+ * If the Feign client uses the URL, the original headers are unmodifiable.
+ * @param request feign request
+ * @return modifiable headers
+ */
+ private Map> getModifiableHeaders(Request request) {
+ Map> headers;
+ headers = (Map>) ReflectionUtils.getFieldValue(request, "headers");
+
+ if (!(headers instanceof LinkedHashMap)) {
+ headers = new LinkedHashMap<>(headers);
+ ReflectionUtils.setFieldValue(request, "headers", headers);
+ }
+ return headers;
+ }
+
+ @Override
+ public int getOrder() {
+ return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER;
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java
deleted file mode 100644
index edbf5f0b..00000000
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
- *
- * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
- *
- * Licensed under the BSD 3-Clause License (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * 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.
- *
- */
-
-package com.tencent.cloud.metadata.core;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Map;
-
-import com.tencent.cloud.common.constant.OrderConstant;
-import com.tencent.cloud.common.metadata.MetadataContext;
-import com.tencent.cloud.common.metadata.MetadataContextHolder;
-import com.tencent.cloud.common.util.JacksonUtils;
-import feign.RequestInterceptor;
-import feign.RequestTemplate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.core.Ordered;
-import org.springframework.util.CollectionUtils;
-import org.springframework.web.client.RestTemplate;
-
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
-import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
-import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
-import static java.net.URLEncoder.encode;
-
-/**
- * Interceptor used for adding the metadata in http headers from context when web client
- * is Feign.
- *
- * @author Haotian Zhang
- */
-public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, Ordered {
-
- private static final Logger LOG = LoggerFactory.getLogger(EncodeTransferMedataFeignInterceptor.class);
-
- @Override
- public int getOrder() {
- return OrderConstant.Client.Feign.ENCODE_TRANSFER_METADATA_INTERCEPTOR_ORDER;
- }
-
- @Override
- public void apply(RequestTemplate requestTemplate) {
- // get metadata of current thread
- MetadataContext metadataContext = MetadataContextHolder.get();
- Map customMetadata = metadataContext.getCustomMetadata();
- Map disposableMetadata = metadataContext.getDisposableMetadata();
- Map transHeaders = metadataContext.getTransHeadersKV();
-
- this.buildMetadataHeader(requestTemplate, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
-
- // process custom metadata
- this.buildMetadataHeader(requestTemplate, customMetadata, CUSTOM_METADATA);
-
- // set headers that need to be transmitted from the upstream
- this.buildTransmittedHeader(requestTemplate, transHeaders);
- }
-
- private void buildTransmittedHeader(RequestTemplate requestTemplate, Map transHeaders) {
- if (!CollectionUtils.isEmpty(transHeaders)) {
- transHeaders.entrySet().stream().forEach(entry -> {
- requestTemplate.removeHeader(entry.getKey());
- requestTemplate.header(entry.getKey(), entry.getValue());
- });
- }
- }
-
- /**
- * Set metadata into the request header for {@link RestTemplate} .
- * @param requestTemplate instance of {@link RestTemplate}
- * @param metadata metadata map .
- * @param headerName target metadata http header name .
- */
- private void buildMetadataHeader(RequestTemplate requestTemplate, Map metadata, String headerName) {
- if (!CollectionUtils.isEmpty(metadata)) {
- String encodedMetadata = JacksonUtils.serialize2Json(metadata);
- requestTemplate.removeHeader(headerName);
- try {
- requestTemplate.header(headerName, encode(encodedMetadata, UTF_8));
- }
- catch (UnsupportedEncodingException e) {
- LOG.error("Set header failed.", e);
- requestTemplate.header(headerName, encodedMetadata);
- }
- }
- }
-}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java
similarity index 60%
rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java
rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java
index cbf4901b..50e42885 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java
@@ -18,49 +18,53 @@
package com.tencent.cloud.metadata.core;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.Map;
-import com.tencent.cloud.common.constant.OrderConstant;
+import com.google.common.collect.ImmutableMap;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType;
+import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataType;
-import org.springframework.core.Ordered;
import org.springframework.http.HttpRequest;
-import org.springframework.http.client.ClientHttpRequestExecution;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
/**
- * Interceptor used for adding the metadata in http headers from context when web client
- * is RestTemplate.
+ * Pre EnhancedPlugin for rest template to encode transfer metadata.
*
- * @author Haotian Zhang
+ * @author Shedfree Wu
*/
-public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRequestInterceptor, Ordered {
-
+public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedPlugin {
@Override
- public int getOrder() {
- return OrderConstant.Client.RestTemplate.ENCODE_TRANSFER_METADATA_INTERCEPTOR_ORDER;
+ public EnhancedPluginType getType() {
+ return EnhancedPluginType.Client.PRE;
}
@Override
- public ClientHttpResponse intercept(@NonNull HttpRequest httpRequest, @NonNull byte[] bytes,
- @NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+ public void run(EnhancedPluginContext context) throws Throwable {
+ if (!(context.getOriginRequest() instanceof HttpRequest)) {
+ return;
+ }
+ HttpRequest httpRequest = (HttpRequest) context.getOriginRequest();
+
// get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get();
Map customMetadata = metadataContext.getCustomMetadata();
Map disposableMetadata = metadataContext.getDisposableMetadata();
Map transHeaders = metadataContext.getTransHeadersKV();
+ MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
+ Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
+ // currently only support transitive header from calleeMessageMetadataContainer
+ this.buildHeaderMap(httpRequest, calleeTransitiveHeaders);
// build custom disposable metadata request header
this.buildMetadataHeader(httpRequest, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
@@ -70,8 +74,6 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe
// set headers that need to be transmitted from the upstream
this.buildTransmittedHeader(httpRequest, transHeaders);
-
- return clientHttpRequestExecution.execute(httpRequest, bytes);
}
private void buildTransmittedHeader(HttpRequest request, Map transHeaders) {
@@ -82,6 +84,12 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe
}
}
+ private void buildHeaderMap(HttpRequest request, Map headerMap) {
+ if (!CollectionUtils.isEmpty(headerMap)) {
+ headerMap.forEach((key, value) -> request.getHeaders().set(key, UrlUtils.encode(value)));
+ }
+ }
+
/**
* Set metadata into the request header for {@link HttpRequest} .
*
@@ -91,13 +99,12 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe
*/
private void buildMetadataHeader(HttpRequest request, Map metadata, String headerName) {
if (!CollectionUtils.isEmpty(metadata)) {
- String encodedMetadata = JacksonUtils.serialize2Json(metadata);
- try {
- request.getHeaders().set(headerName, URLEncoder.encode(encodedMetadata, UTF_8));
- }
- catch (UnsupportedEncodingException e) {
- request.getHeaders().set(headerName, encodedMetadata);
- }
+ buildHeaderMap(request, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata)));
}
}
+
+ @Override
+ public int getOrder() {
+ return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER;
+ }
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java
similarity index 58%
rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java
rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java
index 2eacd21a..8c996bbf 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java
@@ -18,42 +18,46 @@
package com.tencent.cloud.metadata.core;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.Map;
+import com.google.common.collect.ImmutableMap;
import com.tencent.cloud.common.constant.MetadataConstant;
-import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
-import reactor.core.publisher.Mono;
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType;
+import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataType;
-import org.springframework.cloud.gateway.filter.GatewayFilterChain;
-import org.springframework.cloud.gateway.filter.GlobalFilter;
-import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
/**
- * Scg filter used for writing metadata in HTTP request header.
+ * Pre EnhancedPlugin for scg to encode transfer metadata.
*
- * @author Haotian Zhang
+ * @author Shedfree Wu
*/
-public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered {
-
+public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin {
@Override
- public int getOrder() {
- return OrderConstant.Client.Scg.ENCODE_TRANSFER_METADATA_FILTER_ORDER;
+ public EnhancedPluginType getType() {
+ return EnhancedPluginType.Client.PRE;
}
@Override
- public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ public void run(EnhancedPluginContext context) throws Throwable {
+ if (!(context.getOriginRequest() instanceof ServerWebExchange)) {
+ return;
+ }
+ ServerWebExchange exchange = (ServerWebExchange) context.getOriginRequest();
+
// get request builder
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
@@ -66,11 +70,22 @@ public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered {
Map customMetadata = metadataContext.getCustomMetadata();
Map disposableMetadata = metadataContext.getDisposableMetadata();
+ MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
+ Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
+ // currently only support transitive header from calleeMessageMetadataContainer
+ this.buildHeaderMap(builder, calleeTransitiveHeaders);
+
this.buildMetadataHeader(builder, customMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(builder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
-
TransHeadersTransfer.transfer(exchange.getRequest());
- return chain.filter(exchange.mutate().request(builder.build()).build());
+
+ context.setOriginRequest(exchange.mutate().request(builder.build()).build());
+ }
+
+ private void buildHeaderMap(ServerHttpRequest.Builder builder, Map headerMap) {
+ if (!CollectionUtils.isEmpty(headerMap)) {
+ headerMap.forEach((key, value) -> builder.header(key, UrlUtils.encode(value)));
+ }
}
/**
@@ -81,13 +96,12 @@ public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered {
*/
private void buildMetadataHeader(ServerHttpRequest.Builder builder, Map metadata, String headerName) {
if (!CollectionUtils.isEmpty(metadata)) {
- String encodedMetadata = JacksonUtils.serialize2Json(metadata);
- try {
- builder.header(headerName, URLEncoder.encode(encodedMetadata, UTF_8));
- }
- catch (UnsupportedEncodingException e) {
- builder.header(headerName, encodedMetadata);
- }
+ buildHeaderMap(builder, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata)));
}
}
+
+ @Override
+ public int getOrder() {
+ return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER;
+ }
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java
similarity index 57%
rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilter.java
rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java
index 3547add0..2967a2b4 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilter.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java
@@ -18,48 +18,61 @@
package com.tencent.cloud.metadata.core;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.Map;
+import com.google.common.collect.ImmutableMap;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
-import reactor.core.publisher.Mono;
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
+import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType;
+import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.client.ClientRequest;
-import org.springframework.web.reactive.function.client.ClientResponse;
-import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
-import org.springframework.web.reactive.function.client.ExchangeFunction;
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
/**
- * web client filter used for writing metadata in HTTP request header.
+ * Pre EnhancedPlugin for web client to encode transfer metadata.
*
- * @author sean yu
+ * @author Shedfree Wu
*/
-public class EncodeTransferMedataWebClientFilter implements ExchangeFilterFunction {
+public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlugin {
+ @Override
+ public EnhancedPluginType getType() {
+ return EnhancedPluginType.Client.PRE;
+ }
@Override
- public Mono filter(ClientRequest clientRequest, ExchangeFunction next) {
+ public void run(EnhancedPluginContext context) throws Throwable {
+ if (!(context.getOriginRequest() instanceof ClientRequest)) {
+ return;
+ }
+ ClientRequest clientRequest = (ClientRequest) context.getOriginRequest();
+
MetadataContext metadataContext = MetadataContextHolder.get();
Map customMetadata = metadataContext.getCustomMetadata();
Map disposableMetadata = metadataContext.getDisposableMetadata();
Map transHeaders = metadataContext.getTransHeadersKV();
+ MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
+ Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
ClientRequest.Builder requestBuilder = ClientRequest.from(clientRequest);
+ // currently only support transitive header from calleeMessageMetadataContainer
+ this.buildHeaderMap(requestBuilder, calleeTransitiveHeaders);
+
this.buildMetadataHeader(requestBuilder, customMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(requestBuilder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
this.buildTransmittedHeader(requestBuilder, transHeaders);
- ClientRequest request = requestBuilder.build();
-
- return next.exchange(request);
+ context.setOriginRequest(requestBuilder.build());
}
private void buildTransmittedHeader(ClientRequest.Builder requestBuilder, Map transHeaders) {
@@ -68,6 +81,11 @@ public class EncodeTransferMedataWebClientFilter implements ExchangeFilterFuncti
}
}
+ private void buildHeaderMap(ClientRequest.Builder requestBuilder, Map headerMap) {
+ if (!CollectionUtils.isEmpty(headerMap)) {
+ headerMap.forEach((key, value) -> requestBuilder.header(key, UrlUtils.encode(value)));
+ }
+ }
/**
* Set metadata into the request header for {@link ClientRequest} .
@@ -77,14 +95,12 @@ public class EncodeTransferMedataWebClientFilter implements ExchangeFilterFuncti
*/
private void buildMetadataHeader(ClientRequest.Builder requestBuilder, Map metadata, String headerName) {
if (!CollectionUtils.isEmpty(metadata)) {
- String encodedMetadata = JacksonUtils.serialize2Json(metadata);
- try {
- requestBuilder.header(headerName, URLEncoder.encode(encodedMetadata, UTF_8));
- }
- catch (UnsupportedEncodingException e) {
- requestBuilder.header(headerName, encodedMetadata);
- }
+ buildHeaderMap(requestBuilder, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata)));
}
}
+ @Override
+ public int getOrder() {
+ return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER;
+ }
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ReactiveMetadataProvider.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ReactiveMetadataProvider.java
new file mode 100644
index 00000000..d39d74ed
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ReactiveMetadataProvider.java
@@ -0,0 +1,71 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ *
+ */
+
+package com.tencent.cloud.metadata.provider;
+
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataProvider;
+
+import org.springframework.http.server.reactive.ServerHttpRequest;
+
+/**
+ * MetadataProvider used for Reactive.
+ *
+ * @author Shedfree Wu
+ */
+public class ReactiveMetadataProvider implements MetadataProvider {
+
+ private ServerHttpRequest serverHttpRequest;
+
+ private String callerIp;
+
+ public ReactiveMetadataProvider(ServerHttpRequest serverHttpRequest, String callerIp) {
+ this.serverHttpRequest = serverHttpRequest;
+ this.callerIp = callerIp;
+ }
+
+ @Override
+ public String getRawMetadataStringValue(String key) {
+ switch (key) {
+ case MessageMetadataContainer.LABEL_KEY_METHOD:
+ return serverHttpRequest.getMethod().name();
+ case MessageMetadataContainer.LABEL_KEY_PATH:
+ return UrlUtils.decode(serverHttpRequest.getPath().toString());
+ case MessageMetadataContainer.LABEL_KEY_CALLER_IP:
+ return callerIp;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String getRawMetadataMapValue(String key, String mapKey) {
+ switch (key) {
+ case MessageMetadataContainer.LABEL_MAP_KEY_HEADER:
+ return UrlUtils.decode(SpringWebExpressionLabelUtils.getHeaderValue(serverHttpRequest, mapKey, null));
+ case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE:
+ return UrlUtils.decode(SpringWebExpressionLabelUtils.getCookieValue(serverHttpRequest, mapKey, null));
+ case MessageMetadataContainer.LABEL_MAP_KEY_QUERY:
+ return UrlUtils.decode(SpringWebExpressionLabelUtils.getQueryValue(serverHttpRequest, mapKey, null));
+ default:
+ return null;
+ }
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java
new file mode 100644
index 00000000..b68c88ab
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java
@@ -0,0 +1,71 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ *
+ */
+
+package com.tencent.cloud.metadata.provider;
+
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
+import com.tencent.cloud.common.util.expresstion.ServletExpressionLabelUtils;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataProvider;
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * MetadataProvider used for Servlet.
+ *
+ * @author Shedfree Wu
+ */
+public class ServletMetadataProvider implements MetadataProvider {
+
+ private HttpServletRequest httpServletRequest;
+
+ private String callerIp;
+
+ public ServletMetadataProvider(HttpServletRequest httpServletRequest, String callerIp) {
+ this.httpServletRequest = httpServletRequest;
+ this.callerIp = callerIp;
+ }
+
+ @Override
+ public String getRawMetadataStringValue(String key) {
+ switch (key) {
+ case MessageMetadataContainer.LABEL_KEY_METHOD:
+ return httpServletRequest.getMethod();
+ case MessageMetadataContainer.LABEL_KEY_PATH:
+ return UrlUtils.decode(httpServletRequest.getRequestURI());
+ case MessageMetadataContainer.LABEL_KEY_CALLER_IP:
+ return callerIp;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String getRawMetadataMapValue(String key, String mapKey) {
+ switch (key) {
+ case MessageMetadataContainer.LABEL_MAP_KEY_HEADER:
+ return UrlUtils.decode(httpServletRequest.getHeader(mapKey));
+ case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE:
+ return UrlUtils.decode(ServletExpressionLabelUtils.getCookieValue(httpServletRequest.getCookies(), mapKey, null));
+ case MessageMetadataContainer.LABEL_MAP_KEY_QUERY:
+ return UrlUtils.decode(ExpressionLabelUtils.getQueryValue(httpServletRequest.getQueryString(), mapKey, null));
+ default:
+ return null;
+ }
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java
index 12073bc9..4da4dc8d 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java
@@ -18,24 +18,18 @@
package com.tencent.cloud.metadata.config;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor;
-import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor;
-import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientFilter;
+import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignEnhancedPlugin;
+import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedPlugin;
+import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin;
+import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
-import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class MetadataTransferAutoConfigurationTest {
private final ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner();
+ private final ReactiveWebApplicationContextRunner reactiveWebApplicationContextRunner = new ReactiveWebApplicationContextRunner();
/**
* No any web application.
@@ -57,36 +52,26 @@ public class MetadataTransferAutoConfigurationTest {
this.applicationContextRunner.withConfiguration(AutoConfigurations.of(MetadataTransferAutoConfiguration.class))
.run(context -> {
assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferFeignInterceptorConfig.class);
- assertThat(context).hasSingleBean(EncodeTransferMedataFeignInterceptor.class);
+ assertThat(context).hasSingleBean(EncodeTransferMedataFeignEnhancedPlugin.class);
+ assertThat(context).hasSingleBean(EncodeTransferMedataRestTemplateEnhancedPlugin.class);
assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.class);
- assertThat(context).hasSingleBean(EncodeTransferMedataRestTemplateInterceptor.class);
assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferScgFilterConfig.class);
- assertThat(context).hasSingleBean(GlobalFilter.class);
- assertThat(context).hasSingleBean(EncodeTransferMedataWebClientFilter.class);
});
}
-
+ /**
+ * Reactive web application.
+ */
@Test
public void test2() {
- this.applicationContextRunner
- .withConfiguration(
- AutoConfigurations.of(MetadataTransferAutoConfiguration.class, RestTemplateConfiguration.class))
+ this.reactiveWebApplicationContextRunner.withConfiguration(AutoConfigurations.of(MetadataTransferAutoConfiguration.class, PolarisContextProperties.class))
.run(context -> {
- assertThat(context).hasSingleBean(EncodeTransferMedataFeignInterceptor.class);
- EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor = context.getBean(EncodeTransferMedataRestTemplateInterceptor.class);
- Map restTemplateMap = context.getBeansOfType(RestTemplate.class);
- assertThat(restTemplateMap.size()).isEqualTo(2);
- for (String beanName : Arrays.asList("restTemplate", "loadBalancedRestTemplate")) {
- RestTemplate restTemplate = restTemplateMap.get(beanName);
- assertThat(restTemplate).isNotNull();
- List encodeTransferMedataFeignInterceptorList = restTemplate.getInterceptors()
- .stream()
- .filter(interceptor -> Objects.equals(interceptor, encodeTransferMedataRestTemplateInterceptor))
- .collect(Collectors.toList());
- //EncodeTransferMetadataFeignInterceptor is not added repeatedly
- assertThat(encodeTransferMedataFeignInterceptorList.size()).isEqualTo(1);
- }
+ assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferFeignInterceptorConfig.class);
+ assertThat(context).hasSingleBean(EncodeTransferMedataFeignEnhancedPlugin.class);
+ assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.class);
+ assertThat(context).hasSingleBean(EncodeTransferMedataRestTemplateEnhancedPlugin.class);
+ assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferScgFilterConfig.class);
+ assertThat(context).hasSingleBean(EncodeTransferMedataWebClientEnhancedPlugin.class);
});
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java
index 18886a91..ff27c0c4 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java
@@ -20,6 +20,7 @@ package com.tencent.cloud.metadata.core;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -44,7 +45,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = MOCK,
classes = DecodeTransferMetadataServletFilterTest.TestApplication.class,
- properties = {"spring.config.location = classpath:application-test.yml"})
+ properties = {"spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive"})
public class DecodeTransferMetadataReactiveFilterTest {
@Autowired
@@ -54,7 +55,7 @@ public class DecodeTransferMetadataReactiveFilterTest {
@BeforeEach
public void setUp() {
- this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter();
+ this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(new PolarisContextProperties());
}
@Test
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java
index a457b77d..dd21bce8 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java
@@ -44,7 +44,9 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = RANDOM_PORT,
classes = DecodeTransferMetadataServletFilterTest.TestApplication.class,
- properties = {"spring.config.location = classpath:application-test.yml"})
+ properties = {"spring.config.location = classpath:application-test.yml",
+ "spring.main.web-application-type = servlet",
+ "spring.cloud.gateway.enabled = false"})
public class DecodeTransferMetadataServletFilterTest {
@Autowired
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java
index 93e55378..e34f4e9a 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java
@@ -40,14 +40,16 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
/**
- * Test for {@link EncodeTransferMedataFeignInterceptor}.
+ * Test for {@link EncodeTransferMedataFeignEnhancedPlugin}.
*
* @author Haotian Zhang
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = DEFINED_PORT,
classes = EncodeTransferMedataFeignInterceptorTest.TestApplication.class,
- properties = {"server.port=48081", "spring.config.location = classpath:application-test.yml"})
+ properties = {"server.port=48081", "spring.config.location = classpath:application-test.yml",
+ "spring.main.web-application-type = servlet",
+ "spring.cloud.gateway.enabled = false"})
public class EncodeTransferMedataFeignInterceptorTest {
@Autowired
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java
index cb82b56e..b3c5581a 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java
@@ -17,8 +17,14 @@
package com.tencent.cloud.metadata.core;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Map;
+
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner;
+import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -26,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
@@ -39,14 +46,15 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
- * Test for {@link EncodeTransferMedataRestTemplateInterceptor}.
+ * Test for {@link EncodeTransferMedataRestTemplateEnhancedPlugin}.
*
* @author Haotian Zhang
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = RANDOM_PORT,
classes = EncodeTransferMedataRestTemplateInterceptorTest.TestApplication.class,
- properties = {"spring.config.location = classpath:application-test.yml"})
+ properties = {"spring.config.location = classpath:application-test.yml",
+ "spring.main.web-application-type = reactive"})
public class EncodeTransferMedataRestTemplateInterceptorTest {
@Autowired
@@ -71,7 +79,13 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
@Bean
public RestTemplate restTemplate() {
- return new RestTemplate();
+
+ EncodeTransferMedataRestTemplateEnhancedPlugin plugin = new EncodeTransferMedataRestTemplateEnhancedPlugin();
+ EnhancedRestTemplateInterceptor interceptor = new EnhancedRestTemplateInterceptor(
+ new DefaultEnhancedPluginRunner(Arrays.asList(plugin), new MockRegistration(), null));
+ RestTemplate template = new RestTemplate();
+ template.setInterceptors(Arrays.asList(interceptor));
+ return template;
}
@RequestMapping("/test")
@@ -79,4 +93,37 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b");
}
}
+
+ static class MockRegistration implements Registration {
+
+ @Override
+ public String getServiceId() {
+ return "test";
+ }
+
+ @Override
+ public String getHost() {
+ return "localhost";
+ }
+
+ @Override
+ public int getPort() {
+ return 0;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return false;
+ }
+
+ @Override
+ public URI getUri() {
+ return null;
+ }
+
+ @Override
+ public Map getMetadata() {
+ return null;
+ }
+ }
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java
index bdfcfb44..cbc1116b 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java
@@ -13,65 +13,134 @@
* 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.
+ *
*/
package com.tencent.cloud.metadata.core;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.Map;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
import com.tencent.cloud.common.constant.MetadataConstant;
-import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.metadata.StaticMetadataManager;
+import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner;
+import com.tencent.cloud.rpc.enhancement.scg.EnhancedGatewayGlobalFilter;
+import org.assertj.core.util.Maps;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+import reactor.core.publisher.Mono;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.route.Route;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.web.server.ServerWebExchange;
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
+import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
+import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
+import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
+import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
/**
- * Test for {@link EncodeTransferMedataScgFilter}.
- *
- * @author quan
+ * Test for {@link EncodeTransferMedataScgEnhancedPlugin}.
+ * @author quan, Shedfree Wu
*/
-@ExtendWith(SpringExtension.class)
-@SpringBootTest(webEnvironment = RANDOM_PORT, classes = EncodeTransferMedataScgFilterTest.TestApplication.class,
- properties = {"spring.config.location = classpath:application-test.yml",
- "spring.main.web-application-type = reactive"})
+@ExtendWith(MockitoExtension.class)
public class EncodeTransferMedataScgFilterTest {
- @Autowired
- private ApplicationContext applicationContext;
-
+ private static MockedStatic mockedApplicationContextAwareUtils;
@Mock
- private GatewayFilterChain chain;
+ Registration registration;
+ @Mock
+ GatewayFilterChain chain;
- @Test
- public void testTransitiveMetadataFromApplicationConfig() throws UnsupportedEncodingException {
- EncodeTransferMedataScgFilter filter = applicationContext.getBean(EncodeTransferMedataScgFilter.class);
- MockServerHttpRequest.BaseBuilder> builder = MockServerHttpRequest.get("");
- MockServerWebExchange exchange = MockServerWebExchange.from(builder);
- filter.filter(exchange, chain);
- String metadataStr = exchange.getRequest().getHeaders().getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA);
- String decode = URLDecoder.decode(metadataStr, UTF_8);
- Map transitiveMap = JacksonUtils.deserialize2Map(decode);
- assertThat(transitiveMap.size()).isEqualTo(1);
- assertThat(transitiveMap.get("b")).isEqualTo("2");
+ @BeforeAll
+ static void beforeAll() {
+ mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn("unit-test");
+ ApplicationContext applicationContext = mock(ApplicationContext.class);
+ MetadataLocalProperties metadataLocalProperties = mock(MetadataLocalProperties.class);
+ StaticMetadataManager staticMetadataManager = mock(StaticMetadataManager.class);
+ doReturn(metadataLocalProperties).when(applicationContext).getBean(MetadataLocalProperties.class);
+ doReturn(staticMetadataManager).when(applicationContext).getBean(StaticMetadataManager.class);
+ mockedApplicationContextAwareUtils.when(ApplicationContextAwareUtils::getApplicationContext)
+ .thenReturn(applicationContext);
}
- @SpringBootApplication
- protected static class TestApplication {
+ @AfterAll
+ static void afterAll() {
+ mockedApplicationContextAwareUtils.close();
+ }
+
+ @BeforeEach
+ void setUp() {
+ MetadataContext.LOCAL_NAMESPACE = NAMESPACE_TEST;
+ MetadataContext.LOCAL_SERVICE = SERVICE_PROVIDER;
+ }
+
+ @Test
+ public void testRun() throws URISyntaxException {
+
+ Route route = mock(Route.class);
+ URI uri = new URI("http://TEST/");
+ doReturn(uri).when(route).getUri();
+
+ MetadataContext metadataContext = MetadataContextHolder.get();
+ metadataContext.setTransitiveMetadata(Maps.newHashMap("t-key", "t-value"));
+ metadataContext.setDisposableMetadata(Maps.newHashMap("d-key", "d-value"));
+
+ MockServerHttpRequest mockServerHttpRequest = MockServerHttpRequest.get("/test").build();
+
+ EncodeTransferMedataScgEnhancedPlugin plugin = new EncodeTransferMedataScgEnhancedPlugin();
+ plugin.getOrder();
+ EnhancedGatewayGlobalFilter filter = new EnhancedGatewayGlobalFilter(new DefaultEnhancedPluginRunner(Arrays.asList(plugin), registration, null));
+ filter.getOrder();
+
+ MockServerWebExchange mockServerWebExchange = MockServerWebExchange.builder(mockServerHttpRequest).build();
+ mockServerWebExchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
+ mockServerWebExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, new URI("http://0.0.0.0/"));
+ mockServerWebExchange.getAttributes().put(MetadataConstant.HeaderName.METADATA_CONTEXT, metadataContext);
+ doReturn(Mono.empty()).when(chain).filter(any());
+
+
+ filter.filter(mockServerWebExchange, chain).block();
+
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class);
+ // capture the result exchange
+ Mockito.verify(chain).filter(captor.capture());
+ ServerWebExchange filteredExchange = captor.getValue();
+
+ assertThat(filteredExchange.getRequest().getHeaders().get(CUSTOM_METADATA)).isNotNull();
+ assertThat(filteredExchange.getRequest().getHeaders().get(CUSTOM_DISPOSABLE_METADATA)).isNotNull();
+
+ // test metadataContext init in EnhancedPlugin
+ mockServerWebExchange.getAttributes().remove(MetadataConstant.HeaderName.METADATA_CONTEXT);
+ assertThatCode(() -> filter.filter(mockServerWebExchange, chain).block()).doesNotThrowAnyException();
}
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java
index cb5effc6..dc10917c 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java
@@ -37,18 +37,21 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
- * Test for {@link EncodeTransferMedataWebClientFilter}.
+ * Test for {@link EncodeTransferMedataWebClientEnhancedPlugin}.
*
* @author sean yu
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = RANDOM_PORT,
classes = EncodeTransferMedataWebClientFilterTest.TestApplication.class,
- properties = {"spring.config.location = classpath:application-test.yml"})
+ properties = {"spring.config.location = classpath:application-test.yml",
+ "spring.main.web-application-type = reactive"})
public class EncodeTransferMedataWebClientFilterTest {
@Autowired
private WebClient.Builder webClientBuilder;
+ @LocalServerPort
+ private int localServerPort;
@Test
public void testTransitiveMetadataFromApplicationConfig() {
@@ -63,10 +66,6 @@ public class EncodeTransferMedataWebClientFilterTest {
assertThat(metadata).isEqualTo("2");
}
- @LocalServerPort
- private int localServerPort;
-
-
@SpringBootApplication
@RestController
protected static class TestApplication {
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/provider/MetadataProviderTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/provider/MetadataProviderTest.java
new file mode 100644
index 00000000..dab78d99
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/provider/MetadataProviderTest.java
@@ -0,0 +1,139 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ *
+ */
+
+package com.tencent.cloud.metadata.provider;
+
+import com.tencent.cloud.common.util.UrlUtils;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.http.HttpCookie;
+import org.springframework.http.HttpMethod;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.MockCookie;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for {@link ReactiveMetadataProvider} and {@link ServletMetadataProvider}.
+ *
+ * @author quan, Shedfree Wu
+ */
+public class MetadataProviderTest {
+
+ private static final String notExistKey = "empty";
+
+ @Test
+ public void testReactiveMetadataProvider() {
+ String headerKey1 = "header1";
+ String headerKey2 = "header2";
+ String headerValue1 = "value1";
+ String headerValue2 = "value2/test";
+ String queryKey1 = "qk1";
+ String queryKey2 = "qk2";
+ String queryValue1 = "qv1";
+ String queryValue2 = "qv2/test";
+ String cookieKey1 = "ck1";
+ String cookieKey2 = "ck2";
+ String cookieValue1 = "cv1";
+ String cookieValue2 = "cv2/test";
+ String path = "/echo/test";
+ String callerIp = "localhost";
+ MockServerHttpRequest request = MockServerHttpRequest.get(path)
+ .header(headerKey1, headerValue1)
+ .header(headerKey2, UrlUtils.encode(headerValue2))
+ .queryParam(queryKey1, queryValue1)
+ .queryParam(queryKey2, UrlUtils.encode(queryValue2))
+ .cookie(new HttpCookie(cookieKey1, cookieValue1))
+ .cookie(new HttpCookie(cookieKey2, UrlUtils.encode(cookieValue2)))
+ .build();
+
+ ReactiveMetadataProvider reactiveMetadataProvider = new ReactiveMetadataProvider(request, callerIp);
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey1)).isEqualTo(headerValue1);
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey2)).isEqualTo(headerValue2);
+ // com.tencent.polaris.metadata.core.manager.ComposeMetadataProvider.getRawMetadataMapValue need return null when key don't exist
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, notExistKey)).isNull();
+
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey1)).isEqualTo(cookieValue1);
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey2)).isEqualTo(cookieValue2);
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, notExistKey)).isNull();
+
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey1)).isEqualTo(queryValue1);
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey2)).isEqualTo(queryValue2);
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, notExistKey)).isNull();
+ assertThat(reactiveMetadataProvider.getRawMetadataMapValue(notExistKey, queryKey1)).isNull();
+
+ assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_METHOD)).isEqualTo("GET");
+ assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo(path);
+ assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_CALLER_IP)).isEqualTo(callerIp);
+ assertThat(reactiveMetadataProvider.getRawMetadataStringValue(notExistKey)).isNull();
+
+ request = MockServerHttpRequest.get("/echo/" + UrlUtils.decode("a@b")).build();
+ reactiveMetadataProvider = new ReactiveMetadataProvider(request, callerIp);
+ assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo("/echo/a@b");
+ }
+
+ @Test
+ public void testServletMetadataProvider() {
+ String headerKey1 = "header1";
+ String headerKey2 = "header2";
+ String headerValue1 = "value1";
+ String headerValue2 = "value2/test";
+ String queryKey1 = "qk1";
+ String queryKey2 = "qk2";
+ String queryValue1 = "qv1";
+ String queryValue2 = "qv2/test";
+ String cookieKey1 = "ck1";
+ String cookieKey2 = "ck2";
+ String cookieValue1 = "cv1";
+ String cookieValue2 = "cv2/test";
+ String path = "/echo/test";
+ String callerIp = "localhost";
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader(headerKey1, headerValue1);
+ request.addHeader(headerKey2, UrlUtils.encode(headerValue2));
+ request.setCookies(new MockCookie(cookieKey1, cookieValue1), new MockCookie(cookieKey2, UrlUtils.encode(cookieValue2)));
+ request.setMethod(HttpMethod.GET.name());
+ request.setRequestURI(path);
+ request.setQueryString(queryKey1 + "=" + queryValue1 + "&" + queryKey2 + "=" + UrlUtils.encode(queryValue2));
+
+ ServletMetadataProvider servletMetadataProvider = new ServletMetadataProvider(request, callerIp);
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey1)).isEqualTo(headerValue1);
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey2)).isEqualTo(headerValue2);
+ // com.tencent.polaris.metadata.core.manager.ComposeMetadataProvider.getRawMetadataMapValue need return null when key don't exist
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, notExistKey)).isNull();
+
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey1)).isEqualTo(cookieValue1);
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey2)).isEqualTo(cookieValue2);
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, notExistKey)).isNull();
+
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey1)).isEqualTo(queryValue1);
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey2)).isEqualTo(queryValue2);
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, notExistKey)).isNull();
+ assertThat(servletMetadataProvider.getRawMetadataMapValue(notExistKey, queryKey1)).isNull();
+
+ assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_METHOD)).isEqualTo("GET");
+ assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo(path);
+ assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_CALLER_IP)).isEqualTo(callerIp);
+ assertThat(servletMetadataProvider.getRawMetadataStringValue(notExistKey)).isNull();
+
+ request.setRequestURI("/echo/" + UrlUtils.decode("a@b"));
+ assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo("/echo/a@b");
+ }
+}
diff --git a/spring-cloud-tencent-commons/pom.xml b/spring-cloud-tencent-commons/pom.xml
index 6cc5556b..8632ccdd 100644
--- a/spring-cloud-tencent-commons/pom.xml
+++ b/spring-cloud-tencent-commons/pom.xml
@@ -24,8 +24,17 @@
com.tencent.polaris
polaris-model
+
+ com.tencent.polaris
+ polaris-metadata
+
+
+ com.tencent.cloud
+ spring-cloud-starter-tencent-threadlocal-plugin
+
+
org.springframework.boot
spring-boot-autoconfigure
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
index 8fcd8985..11b667c1 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
@@ -21,22 +21,25 @@ package com.tencent.cloud.common.metadata;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.DiscoveryUtil;
-import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.polaris.metadata.core.MetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataMapValue;
+import com.tencent.polaris.metadata.core.MetadataObjectValue;
+import com.tencent.polaris.metadata.core.MetadataStringValue;
+import com.tencent.polaris.metadata.core.MetadataType;
+import com.tencent.polaris.metadata.core.MetadataValue;
+import com.tencent.polaris.metadata.core.TransitiveType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
-/**
- * Metadata Context.
- *
- * @author Haotian Zhang
- */
-public class MetadataContext {
+public class MetadataContext extends com.tencent.polaris.metadata.core.manager.MetadataContext {
/**
* transitive context.
@@ -63,6 +66,11 @@ public class MetadataContext {
*/
public static final String FRAGMENT_RAW_TRANSHEADERS_KV = "trans-headers-kv";
+ /**
+ * the key of the header(key-value) list needed to be store as loadbalance data.
+ */
+ public static final String FRAGMENT_LB_METADATA = "load-balance-metadata";
+
private static final Logger LOG = LoggerFactory.getLogger(MetadataContext.class);
/**
* Namespace of local instance.
@@ -108,22 +116,70 @@ public class MetadataContext {
LOCAL_SERVICE = serviceName;
}
- private final Map> fragmentContexts;
+ public MetadataContext() {
+ super(MetadataConstant.POLARIS_TRANSITIVE_HEADER_PREFIX);
+ }
- private final Map loadbalancerMetadata;
+ private Map getMetadataAsMap(MetadataType metadataType, TransitiveType transitiveType, boolean downstream) {
+ MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream);
+ Map values = new HashMap<>();
+ metadataContainer.iterateMetadataValues(new BiConsumer() {
+ @Override
+ public void accept(String s, MetadataValue metadataValue) {
+ if (metadataValue instanceof MetadataStringValue) {
+ MetadataStringValue metadataStringValue = (MetadataStringValue) metadataValue;
+ if (metadataStringValue.getTransitiveType() == transitiveType) {
+ values.put(s, metadataStringValue.getStringValue());
+ }
+ }
+ }
+ });
+ return values;
+ }
+ private void putMetadataAsMap(MetadataType metadataType, TransitiveType transitiveType, boolean downstream, Map values) {
+ MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream);
+ for (Map.Entry entry : values.entrySet()) {
+ metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), transitiveType);
+ }
+ }
- public MetadataContext() {
- this.fragmentContexts = new ConcurrentHashMap<>();
- this.loadbalancerMetadata = new ConcurrentHashMap<>();
+ private Map getMapMetadataAsMap(MetadataType metadataType, String mapKey, TransitiveType transitiveType, boolean downstream) {
+ MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream);
+ Map values = new HashMap<>();
+ MetadataValue metadataValue = metadataContainer.getMetadataValue(mapKey);
+ if (!(metadataValue instanceof MetadataMapValue)) {
+ return values;
+ }
+ MetadataMapValue metadataMapValue = (MetadataMapValue) metadataValue;
+ metadataMapValue.iterateMapValues(new BiConsumer() {
+ @Override
+ public void accept(String s, MetadataValue metadataValue) {
+ if (metadataValue instanceof MetadataStringValue) {
+ MetadataStringValue metadataStringValue = (MetadataStringValue) metadataValue;
+ if (metadataStringValue.getTransitiveType() == transitiveType) {
+ values.put(s, metadataStringValue.getStringValue());
+ }
+ }
+ }
+ });
+ return values;
+ }
+
+ private void putMapMetadataAsMap(MetadataType metadataType, String mapKey,
+ TransitiveType transitiveType, boolean downstream, Map values) {
+ MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream);
+ for (Map.Entry entry : values.entrySet()) {
+ metadataContainer.putMetadataMapValue(mapKey, entry.getKey(), entry.getValue(), transitiveType);
+ }
}
public Map getDisposableMetadata() {
- return this.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE);
+ return getFragmentContext(FRAGMENT_DISPOSABLE);
}
public Map getTransitiveMetadata() {
- return this.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
+ return getFragmentContext(FRAGMENT_TRANSITIVE);
}
public Map getCustomMetadata() {
@@ -140,51 +196,76 @@ public class MetadataContext {
}
public Map getTransHeaders() {
- return this.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS);
+ return this.getFragmentContext(FRAGMENT_RAW_TRANSHEADERS);
}
public Map getTransHeadersKV() {
- return this.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV);
+ return getFragmentContext(FRAGMENT_RAW_TRANSHEADERS_KV);
}
public Map getLoadbalancerMetadata() {
- return this.loadbalancerMetadata;
+ MetadataContainer metadataContainer = getMetadataContainer(MetadataType.APPLICATION, false);
+ MetadataValue metadataValue = metadataContainer.getMetadataValue(FRAGMENT_LB_METADATA);
+ Map values = new HashMap<>();
+ if (metadataValue instanceof MetadataMapValue) {
+ MetadataMapValue metadataMapValue = (MetadataMapValue) metadataValue;
+ metadataMapValue.iterateMapValues(new BiConsumer() {
+ @Override
+ public void accept(String s, MetadataValue metadataValue) {
+ if (metadataValue instanceof MetadataObjectValue) {
+ Optional> objectValue = ((MetadataObjectValue>) metadataValue).getObjectValue();
+ objectValue.ifPresent(o -> values.put(s, o));
+ }
+ }
+ });
+ }
+ return values;
+ }
+
+ public void setLoadbalancer(String key, Object value) {
+ MetadataContainer metadataContainer = getMetadataContainer(MetadataType.APPLICATION, false);
+ metadataContainer.putMetadataMapObjectValue(FRAGMENT_LB_METADATA, key, value);
}
public void setTransitiveMetadata(Map transitiveMetadata) {
- this.putFragmentContext(FRAGMENT_TRANSITIVE, Collections.unmodifiableMap(transitiveMetadata));
+ putFragmentContext(FRAGMENT_TRANSITIVE, Collections.unmodifiableMap(transitiveMetadata));
}
public void setDisposableMetadata(Map disposableMetadata) {
- this.putFragmentContext(FRAGMENT_DISPOSABLE, Collections.unmodifiableMap(disposableMetadata));
+ putFragmentContext(FRAGMENT_DISPOSABLE, Collections.unmodifiableMap(disposableMetadata));
}
public void setUpstreamDisposableMetadata(Map upstreamDisposableMetadata) {
- this.putFragmentContext(FRAGMENT_UPSTREAM_DISPOSABLE, Collections.unmodifiableMap(upstreamDisposableMetadata));
+ putFragmentContext(FRAGMENT_UPSTREAM_DISPOSABLE, Collections.unmodifiableMap(upstreamDisposableMetadata));
}
public void setTransHeadersKV(String key, String value) {
- this.putContext(FRAGMENT_RAW_TRANSHEADERS_KV, key, value);
+ putContext(FRAGMENT_RAW_TRANSHEADERS_KV, key, value);
}
public void setTransHeaders(String key, String value) {
- this.putContext(FRAGMENT_RAW_TRANSHEADERS, key, value);
- }
-
- public void setLoadbalancer(String key, Object value) {
- this.loadbalancerMetadata.put(key, value);
+ putContext(FRAGMENT_RAW_TRANSHEADERS, key, value);
}
public Map getFragmentContext(String fragment) {
- Map fragmentContext = fragmentContexts.get(fragment);
- if (fragmentContext == null) {
- return Collections.emptyMap();
+ switch (fragment) {
+ case FRAGMENT_TRANSITIVE:
+ return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.PASS_THROUGH, false);
+ case FRAGMENT_DISPOSABLE:
+ return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, false);
+ case FRAGMENT_UPSTREAM_DISPOSABLE:
+ return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, true);
+ case FRAGMENT_RAW_TRANSHEADERS:
+ return getMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS, TransitiveType.NONE, false);
+ case FRAGMENT_RAW_TRANSHEADERS_KV:
+ return getMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS_KV, TransitiveType.PASS_THROUGH, false);
+ default:
+ return getMapMetadataAsMap(MetadataType.CUSTOM, fragment, TransitiveType.NONE, false);
}
- return Collections.unmodifiableMap(fragmentContext);
}
public String getContext(String fragment, String key) {
- Map fragmentContext = fragmentContexts.get(fragment);
+ Map fragmentContext = getFragmentContext(fragment);
if (fragmentContext == null) {
return null;
}
@@ -192,22 +273,31 @@ public class MetadataContext {
}
public void putContext(String fragment, String key, String value) {
- Map fragmentContext = fragmentContexts.get(fragment);
- if (fragmentContext == null) {
- fragmentContext = new ConcurrentHashMap<>();
- fragmentContexts.put(fragment, fragmentContext);
- }
- fragmentContext.put(key, value);
+ Map values = new HashMap<>();
+ values.put(key, value);
+ putFragmentContext(fragment, values);
}
public void putFragmentContext(String fragment, Map context) {
- fragmentContexts.put(fragment, context);
- }
-
- @Override
- public String toString() {
- return "MetadataContext{" +
- "fragmentContexts=" + JacksonUtils.serialize2Json(fragmentContexts) +
- '}';
+ switch (fragment) {
+ case FRAGMENT_TRANSITIVE:
+ putMetadataAsMap(MetadataType.CUSTOM, TransitiveType.PASS_THROUGH, false, context);
+ break;
+ case FRAGMENT_DISPOSABLE:
+ putMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, false, context);
+ break;
+ case FRAGMENT_UPSTREAM_DISPOSABLE:
+ putMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, true, context);
+ break;
+ case FRAGMENT_RAW_TRANSHEADERS:
+ putMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS, TransitiveType.NONE, false, context);
+ break;
+ case FRAGMENT_RAW_TRANSHEADERS_KV:
+ putMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS_KV, TransitiveType.PASS_THROUGH, false, context);
+ break;
+ default:
+ putMapMetadataAsMap(MetadataType.CUSTOM, fragment, TransitiveType.NONE, false, context);
+ break;
+ }
}
}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java
index db9d1e5b..71e1b775 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java
@@ -24,6 +24,11 @@ import java.util.Optional;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.polaris.metadata.core.MessageMetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataContainer;
+import com.tencent.polaris.metadata.core.MetadataProvider;
+import com.tencent.polaris.metadata.core.MetadataType;
+import com.tencent.polaris.metadata.core.TransitiveType;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -38,48 +43,52 @@ import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_UPSTREA
*/
public final class MetadataContextHolder {
- private static final ThreadLocal METADATA_CONTEXT = new InheritableThreadLocal<>();
-
private static MetadataLocalProperties metadataLocalProperties;
private static StaticMetadataManager staticMetadataManager;
+ static {
+ com.tencent.polaris.metadata.core.manager.MetadataContextHolder.setInitializer(MetadataContextHolder::createMetadataManager);
+ }
+
private MetadataContextHolder() {
}
- /**
- * Get metadata context. Create if not existing.
- * @return METADATA_CONTEXT
- */
public static MetadataContext get() {
- if (METADATA_CONTEXT.get() != null) {
- return METADATA_CONTEXT.get();
- }
+ return (MetadataContext) com.tencent.polaris.metadata.core.manager.MetadataContextHolder.getOrCreate();
+ }
+ private static MetadataContext createMetadataManager() {
+ MetadataContext metadataManager = new MetadataContext();
if (metadataLocalProperties == null) {
- metadataLocalProperties = ApplicationContextAwareUtils.getApplicationContext().getBean(MetadataLocalProperties.class);
+ metadataLocalProperties = ApplicationContextAwareUtils.getApplicationContext()
+ .getBean(MetadataLocalProperties.class);
}
if (staticMetadataManager == null) {
- staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext().getBean(StaticMetadataManager.class);
+ staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext()
+ .getBean(StaticMetadataManager.class);
+ }
+ MetadataContainer metadataContainer = metadataManager.getMetadataContainer(MetadataType.CUSTOM, false);
+ Map mergedStaticTransitiveMetadata = staticMetadataManager.getMergedStaticTransitiveMetadata();
+ for (Map.Entry entry : mergedStaticTransitiveMetadata.entrySet()) {
+ metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.PASS_THROUGH);
+ }
+ Map mergedStaticDisposableMetadata = staticMetadataManager.getMergedStaticDisposableMetadata();
+ for (Map.Entry entry : mergedStaticDisposableMetadata.entrySet()) {
+ metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE);
}
-
- // init static transitive metadata
- MetadataContext metadataContext = new MetadataContext();
- metadataContext.setTransitiveMetadata(staticMetadataManager.getMergedStaticTransitiveMetadata());
- metadataContext.setDisposableMetadata(staticMetadataManager.getMergedStaticDisposableMetadata());
if (StringUtils.hasText(staticMetadataManager.getTransHeader())) {
- metadataContext.setTransHeaders(staticMetadataManager.getTransHeader(), "");
+ String transHeader = staticMetadataManager.getTransHeader();
+ metadataContainer.putMetadataMapValue(MetadataContext.FRAGMENT_RAW_TRANSHEADERS, transHeader, "", TransitiveType.NONE);
}
-
- METADATA_CONTEXT.set(metadataContext);
-
- return METADATA_CONTEXT.get();
+ return metadataManager;
}
/**
* Get disposable metadata value from thread local .
- * @param key metadata key .
+ *
+ * @param key metadata key .
* @param upstream upstream disposable , otherwise will return local static disposable metadata .
* @return target disposable metadata value .
*/
@@ -95,6 +104,7 @@ public final class MetadataContextHolder {
/**
* Get all disposable metadata value from thread local .
+ *
* @param upstream upstream disposable , otherwise will return local static disposable metadata .
* @return target disposable metadata value .
*/
@@ -112,43 +122,46 @@ public final class MetadataContextHolder {
/**
* Set metadata context.
+ *
* @param metadataContext metadata context
*/
public static void set(MetadataContext metadataContext) {
- METADATA_CONTEXT.set(metadataContext);
+ com.tencent.polaris.metadata.core.manager.MetadataContextHolder.set(metadataContext);
}
/**
* Save metadata map to thread local.
+ *
* @param dynamicTransitiveMetadata custom metadata collection
* @param dynamicDisposableMetadata custom disposable metadata connection
+ * @param callerMetadataProvider caller metadata provider
*/
- public static void init(Map dynamicTransitiveMetadata, Map dynamicDisposableMetadata) {
- // Init ThreadLocal.
- MetadataContextHolder.remove();
- MetadataContext metadataContext = MetadataContextHolder.get();
-
- // Save transitive metadata to ThreadLocal.
- if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) {
- Map staticTransitiveMetadata = metadataContext.getTransitiveMetadata();
- Map mergedTransitiveMetadata = new HashMap<>();
- mergedTransitiveMetadata.putAll(staticTransitiveMetadata);
- mergedTransitiveMetadata.putAll(dynamicTransitiveMetadata);
- metadataContext.setTransitiveMetadata(Collections.unmodifiableMap(mergedTransitiveMetadata));
- }
- if (!CollectionUtils.isEmpty(dynamicDisposableMetadata)) {
- Map mergedUpstreamDisposableMetadata = new HashMap<>(dynamicDisposableMetadata);
- metadataContext.setUpstreamDisposableMetadata(Collections.unmodifiableMap(mergedUpstreamDisposableMetadata));
- }
- Map staticDisposableMetadata = metadataContext.getDisposableMetadata();
- metadataContext.setDisposableMetadata(Collections.unmodifiableMap(staticDisposableMetadata));
- MetadataContextHolder.set(metadataContext);
+ public static void init(Map dynamicTransitiveMetadata, Map dynamicDisposableMetadata,
+ MetadataProvider callerMetadataProvider) {
+ com.tencent.polaris.metadata.core.manager.MetadataContextHolder.refresh(metadataManager -> {
+ MetadataContainer metadataContainerUpstream = metadataManager.getMetadataContainer(MetadataType.CUSTOM, false);
+ if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) {
+ for (Map.Entry entry : dynamicTransitiveMetadata.entrySet()) {
+ metadataContainerUpstream.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.PASS_THROUGH);
+ }
+ }
+ MetadataContainer metadataContainerDownstream = metadataManager.getMetadataContainer(MetadataType.CUSTOM, true);
+ if (!CollectionUtils.isEmpty(dynamicDisposableMetadata)) {
+ for (Map.Entry entry : dynamicDisposableMetadata.entrySet()) {
+ metadataContainerDownstream.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE);
+ }
+ }
+ if (callerMetadataProvider != null) {
+ MessageMetadataContainer callerMessageContainer = metadataManager.getMetadataContainer(MetadataType.MESSAGE, true);
+ callerMessageContainer.setMetadataProvider(callerMetadataProvider);
+ }
+ });
}
/**
* Remove metadata context.
*/
public static void remove() {
- METADATA_CONTEXT.remove();
+ com.tencent.polaris.metadata.core.manager.MetadataContextHolder.remove();
}
}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java
index bc2e20c9..d822b194 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java
@@ -31,12 +31,15 @@ import static java.util.Locale.ENGLISH;
public final class ReflectionUtils extends org.springframework.util.ReflectionUtils {
private final static String SET_PREFIX = "set";
+
private ReflectionUtils() {
}
public static boolean writableBeanField(Field field) {
String fieldName = field.getName();
+
String setMethodName = SET_PREFIX + capitalize(fieldName);
+
return ClassUtils.hasMethod(field.getDeclaringClass(), setMethodName, field.getType());
}
@@ -65,4 +68,20 @@ public final class ReflectionUtils extends org.springframework.util.ReflectionUt
}
return null;
}
+
+ public static void setFieldValue(Object instance, String fieldName, Object value) {
+ Field field = org.springframework.util.ReflectionUtils.findField(instance.getClass(), fieldName);
+ if (field == null) {
+ return;
+ }
+
+ field.setAccessible(true);
+
+ try {
+ setField(field, instance, value);
+ }
+ finally {
+ field.setAccessible(false);
+ }
+ }
}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java
index c7029def..9c5e73b9 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java
@@ -138,12 +138,16 @@ public final class ExpressionLabelUtils {
}
public static String getQueryValue(String queryString, String queryKey) {
+ return getQueryValue(queryString, queryKey, StringUtils.EMPTY);
+ }
+
+ public static String getQueryValue(String queryString, String queryKey, String defaultValue) {
if (StringUtils.isBlank(queryString)) {
- return StringUtils.EMPTY;
+ return defaultValue;
}
String[] queries = StringUtils.split(queryString, "&");
if (queries == null || queries.length == 0) {
- return StringUtils.EMPTY;
+ return defaultValue;
}
for (String query : queries) {
String[] queryKV = StringUtils.split(query, "=");
@@ -151,7 +155,7 @@ public final class ExpressionLabelUtils {
return queryKV[1];
}
}
- return StringUtils.EMPTY;
+ return defaultValue;
}
public static String getFirstValue(Map> valueMaps, String key) {
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java
index ef38fdc1..5c605d81 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java
@@ -83,14 +83,18 @@ public final class ServletExpressionLabelUtils {
}
public static String getCookieValue(Cookie[] cookies, String key) {
+ return getCookieValue(cookies, key, StringUtils.EMPTY);
+ }
+
+ public static String getCookieValue(Cookie[] cookies, String key, String defaultValue) {
if (cookies == null || cookies.length == 0) {
- return StringUtils.EMPTY;
+ return defaultValue;
}
for (Cookie cookie : cookies) {
if (StringUtils.equals(cookie.getName(), key)) {
return cookie.getValue();
}
}
- return StringUtils.EMPTY;
+ return defaultValue;
}
}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java
index b8b1a59a..476aeeec 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java
@@ -129,29 +129,41 @@ public final class SpringWebExpressionLabelUtils {
}
public static String getHeaderValue(ServerHttpRequest request, String key) {
+ return getHeaderValue(request, key, StringUtils.EMPTY);
+ }
+
+ public static String getHeaderValue(ServerHttpRequest request, String key, String defaultValue) {
String value = request.getHeaders().getFirst(key);
if (value == null) {
- return StringUtils.EMPTY;
+ return defaultValue;
}
return value;
}
public static String getQueryValue(ServerHttpRequest request, String key) {
+ return getQueryValue(request, key, StringUtils.EMPTY);
+ }
+
+ public static String getQueryValue(ServerHttpRequest request, String key, String defaultValue) {
MultiValueMap queries = request.getQueryParams();
if (CollectionUtils.isEmpty(queries)) {
- return StringUtils.EMPTY;
+ return defaultValue;
}
String value = queries.getFirst(key);
if (value == null) {
- return StringUtils.EMPTY;
+ return defaultValue;
}
return value;
}
public static String getCookieValue(ServerHttpRequest request, String key) {
+ return getCookieValue(request, key, StringUtils.EMPTY);
+ }
+
+ public static String getCookieValue(ServerHttpRequest request, String key, String defaultValue) {
HttpCookie cookie = request.getCookies().getFirst(key);
if (cookie == null) {
- return StringUtils.EMPTY;
+ return defaultValue;
}
return cookie.getValue();
}
diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java
index fd12133d..c2cd05b8 100644
--- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java
+++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java
@@ -36,7 +36,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = MetadataContextHolderTest.TestApplication.class,
- properties = {"spring.config.location = classpath:application-test.yml"})
+ properties = {"spring.config.location = classpath:application-test.yml",
+ "spring.main.web-application-type = reactive"})
public class MetadataContextHolderTest {
@Test
@@ -62,7 +63,7 @@ public class MetadataContextHolderTest {
customMetadata.put("a", "1");
customMetadata.put("b", "22");
customMetadata.put("c", "3");
- MetadataContextHolder.init(customMetadata, new HashMap<>());
+ MetadataContextHolder.init(customMetadata, new HashMap<>(), null);
metadataContext = MetadataContextHolder.get();
customMetadata = metadataContext.getTransitiveMetadata();
Assertions.assertThat(customMetadata.get("a")).isEqualTo("1");
diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml
index bfd68f21..a39a736c 100644
--- a/spring-cloud-tencent-dependencies/pom.xml
+++ b/spring-cloud-tencent-dependencies/pom.xml
@@ -192,6 +192,12 @@
${revision}
+
+ com.tencent.cloud
+ spring-cloud-starter-tencent-threadlocal-plugin
+ ${revision}
+
+
com.google.guava
diff --git a/spring-cloud-tencent-plugin-starters/pom.xml b/spring-cloud-tencent-plugin-starters/pom.xml
index 0258ceee..5debf636 100644
--- a/spring-cloud-tencent-plugin-starters/pom.xml
+++ b/spring-cloud-tencent-plugin-starters/pom.xml
@@ -19,6 +19,7 @@
spring-cloud-tencent-gateway-plugin
spring-cloud-starter-tencent-discovery-adapter-plugin
spring-cloud-tencent-lossless-plugin
+ spring-cloud-starter-tencent-threadlocal-plugin
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/pom.xml
new file mode 100644
index 00000000..91975c14
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/pom.xml
@@ -0,0 +1,35 @@
+
+
+
+ spring-cloud-tencent-plugin-starters
+ com.tencent.cloud
+ ${revision}
+ ../pom.xml
+
+ 4.0.0
+
+ spring-cloud-starter-tencent-threadlocal-plugin
+ Spring Cloud Starter Tencent ThreadLocal plugin
+
+
+
+
+ com.tencent.polaris
+ polaris-threadlocal
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/main/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapper.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/main/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapper.java
new file mode 100644
index 00000000..f2f9dc1c
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/main/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapper.java
@@ -0,0 +1,45 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+package com.tencent.cloud.plugin.threadlocal;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import com.tencent.polaris.threadlocal.cross.RunnableWrapper;
+
+import org.springframework.core.task.TaskExecutor;
+
+public class TaskExecutorWrapper implements TaskExecutor {
+
+ private final TaskExecutor taskExecutor;
+
+ private final Supplier contextGetter;
+
+ private final Consumer contextSetter;
+
+ public TaskExecutorWrapper(TaskExecutor taskExecutor, Supplier contextGetter, Consumer contextSetter) {
+ this.taskExecutor = taskExecutor;
+ this.contextGetter = contextGetter;
+ this.contextSetter = contextSetter;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ taskExecutor.execute(new RunnableWrapper<>(command, contextGetter, contextSetter));
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/test/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapperTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/test/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapperTest.java
new file mode 100644
index 00000000..15dd313c
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/test/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapperTest.java
@@ -0,0 +1,62 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+package com.tencent.cloud.plugin.threadlocal;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Fail.fail;
+
+/**
+ * Test for {@link TaskExecutorWrapper}.
+ *
+ * @author Haotian Zhang
+ */
+public class TaskExecutorWrapperTest {
+
+ private static final ThreadLocal TEST_THREAD_LOCAL = new ThreadLocal<>();
+
+ private static final String TEST = "TEST";
+
+ @Test
+ public void testExecute() {
+ TEST_THREAD_LOCAL.set(TEST);
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.initialize();
+ AtomicReference result = new AtomicReference<>(false);
+ CountDownLatch latch = new CountDownLatch(1);
+ TaskExecutorWrapper taskExecutorWrapper = new TaskExecutorWrapper<>(
+ executor, TEST_THREAD_LOCAL::get, TEST_THREAD_LOCAL::set);
+ taskExecutorWrapper.execute(() -> {
+ result.set(TEST.equals(TEST_THREAD_LOCAL.get()));
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ assertThat(result.get()).isTrue();
+ }
+ catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ }
+}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java
index b7ee6d11..0735a90d 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java
@@ -21,7 +21,6 @@ import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
-import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType;
@@ -32,11 +31,10 @@ import feign.Request;
import feign.Request.Options;
import feign.Response;
-import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
-import static com.tencent.cloud.rpc.enhancement.resttemplate.PolarisLoadBalancerRequestTransformer.LOAD_BALANCER_SERVICE_INSTANCE;
import static feign.Util.checkNotNull;
/**
@@ -69,10 +67,20 @@ public class EnhancedFeignClient implements Client {
.url(url)
.build();
enhancedPluginContext.setRequest(enhancedRequestContext);
+ enhancedPluginContext.setOriginRequest(request);
enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance());
- enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get()
- .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), url);
+ String svcName = request.requestTemplate().feignTarget().name();
+ DefaultServiceInstance serviceInstance = new DefaultServiceInstance(
+ String.format("%s-%s-%d", svcName, url.getHost(), url.getPort()),
+ svcName, url.getHost(), url.getPort(), url.getScheme().equals("https"));
+ // -1 means access directly by url
+ if (serviceInstance.getPort() == -1) {
+ enhancedPluginContext.setTargetServiceInstance(null, url);
+ }
+ else {
+ enhancedPluginContext.setTargetServiceInstance(serviceInstance, url);
+ }
// Run pre enhanced plugins.
pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java
index 7eb14ae8..e9ef0cc6 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java
@@ -36,6 +36,8 @@ public class EnhancedPluginContext {
private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedPluginContext.class);
+ private Object originRequest;
+
private EnhancedRequestContext request;
private EnhancedResponseContext response;
@@ -51,6 +53,14 @@ public class EnhancedPluginContext {
*/
private ServiceInstance targetServiceInstance;
+ public Object getOriginRequest() {
+ return originRequest;
+ }
+
+ public void setOriginRequest(Object originRequest) {
+ this.originRequest = originRequest;
+ }
+
public EnhancedRequestContext getRequest() {
return request;
}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java
index 6677dac0..eff88f13 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java
@@ -44,5 +44,19 @@ public class PluginOrderConstant {
* {@link com.tencent.cloud.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter}.
*/
public static final int CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 2;
+
+ /**
+ * order for
+ * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataFeignEnhancedPlugin}
+ * and
+ * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataScgEnhancedPlugin}
+ * and
+ * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin}
+ * and
+ * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataZuulEnhancedPlugin}
+ * and
+ * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedPlugin}.
+ */
+ public static final int CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
}
}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java
index c50cbb90..6241ac59 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java
@@ -58,6 +58,7 @@ public class EnhancedRestTemplateInterceptor implements ClientHttpRequestInterce
.url(request.getURI())
.build();
enhancedPluginContext.setRequest(enhancedRequestContext);
+ enhancedPluginContext.setOriginRequest(request);
enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance());
enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get()
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java
index a838b646..7b65f2e8 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java
@@ -27,15 +27,15 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext;
import reactor.core.publisher.Mono;
-import org.springframework.cloud.client.ServiceInstance;
-import org.springframework.cloud.client.loadbalancer.Response;
+import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.route.Route;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
-import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
/**
* EnhancedGatewayGlobalFilter.
@@ -51,31 +51,40 @@ public class EnhancedGatewayGlobalFilter implements GlobalFilter, Ordered {
}
@Override
- public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ public Mono filter(ServerWebExchange originExchange, GatewayFilterChain chain) {
EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext();
- URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
+
EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder()
- .httpHeaders(exchange.getRequest().getHeaders())
- .httpMethod(exchange.getRequest().getMethod())
- .url(uri)
+ .httpHeaders(originExchange.getRequest().getHeaders())
+ .httpMethod(originExchange.getRequest().getMethod())
+ .url(originExchange.getRequest().getURI())
.build();
enhancedPluginContext.setRequest(enhancedRequestContext);
-
- enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance());
- Response serviceInstanceResponse = exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR);
- if (serviceInstanceResponse != null && serviceInstanceResponse.hasServer()) {
- ServiceInstance instance = serviceInstanceResponse.getServer();
- enhancedPluginContext.setTargetServiceInstance(instance, exchange.getRequest().getURI());
- }
- else {
- enhancedPluginContext.setTargetServiceInstance(null, uri);
- }
+ enhancedPluginContext.setOriginRequest(originExchange);
// Run pre enhanced plugins.
pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
-
+ // Exchange may be changed in plugin
+ ServerWebExchange exchange = (ServerWebExchange) enhancedPluginContext.getOriginRequest();
long startTime = System.currentTimeMillis();
return chain.filter(exchange)
+ .doOnSubscribe(v -> {
+ Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
+ URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
+ enhancedPluginContext.getRequest().setUrl(uri);
+ if (uri != null) {
+ if (route != null && route.getUri().getScheme().contains("lb")) {
+ DefaultServiceInstance serviceInstance = new DefaultServiceInstance();
+ serviceInstance.setServiceId(route.getUri().getHost());
+ serviceInstance.setHost(uri.getHost());
+ serviceInstance.setPort(uri.getPort());
+ enhancedPluginContext.setTargetServiceInstance(serviceInstance, null);
+ }
+ else {
+ enhancedPluginContext.setTargetServiceInstance(null, uri);
+ }
+ }
+ })
.doOnSuccess(v -> {
enhancedPluginContext.setDelay(System.currentTimeMillis() - startTime);
EnhancedResponseContext enhancedResponseContext = EnhancedResponseContext.builder()
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java
index 673cac3d..a215cfe8 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java
@@ -46,23 +46,25 @@ public class EnhancedWebClientExchangeFilterFunction implements ExchangeFilterFu
}
@Override
- public Mono filter(ClientRequest request, ExchangeFunction next) {
+ public Mono filter(ClientRequest originRequest, ExchangeFunction next) {
EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext();
EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder()
- .httpHeaders(request.headers())
- .httpMethod(request.method())
- .url(request.url())
+ .httpHeaders(originRequest.headers())
+ .httpMethod(originRequest.method())
+ .url(originRequest.url())
.build();
enhancedPluginContext.setRequest(enhancedRequestContext);
+ enhancedPluginContext.setOriginRequest(originRequest);
enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance());
enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get()
- .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), request.url());
+ .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), originRequest.url());
// Run post enhanced plugins.
pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
-
+ // request may be changed by plugin
+ ClientRequest request = (ClientRequest) enhancedPluginContext.getOriginRequest();
long startTime = System.currentTimeMillis();
return next.exchange(request)
.doOnSuccess(response -> {
diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java
index d25ea12e..39706816 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java
@@ -39,10 +39,9 @@ import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
-import org.springframework.cloud.client.ServiceInstance;
-import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.route.Route;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -56,8 +55,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
@ExtendWith(MockitoExtension.class)
public class EnhancedGatewayGlobalFilterTest {
@@ -111,20 +110,10 @@ public class EnhancedGatewayGlobalFilterTest {
doReturn(HttpMethod.GET).when(request).getMethod();
doReturn(new HttpHeaders()).when(response).getHeaders();
doReturn(Mono.empty()).when(chain).filter(exchange);
-
- ServiceInstance serviceInstance = mock(ServiceInstance.class);
- Response serviceInstanceResponse = new Response() {
- @Override
- public boolean hasServer() {
- return true;
- }
-
- @Override
- public ServiceInstance getServer() {
- return serviceInstance;
- }
- };
- doReturn(serviceInstanceResponse).when(exchange).getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR);
+ Route route = mock(Route.class);
+ URI uri = new URI("http://TEST/");
+ doReturn(uri).when(route).getUri();
+ doReturn(route).when(exchange).getAttribute(GATEWAY_ROUTE_ATTR);
doReturn(new URI("http://0.0.0.0/")).when(exchange).getAttribute(GATEWAY_REQUEST_URL_ATTR);
doReturn(request).when(exchange).getRequest();
doReturn(response).when(exchange).getResponse();