diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fc2d96db..19325579e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,3 +15,4 @@ - [feat: adapt to polaris-java ReportClientRequestCustomizer plugin for config watch reporting](https://github.com/Tencent/spring-cloud-tencent/pull/1813) - [feat: support audit log](https://github.com/Tencent/spring-cloud-tencent/pull/1812) - [feat: support config effective value ](https://github.com/Tencent/spring-cloud-tencent/pull/1815) +- [feat: support encoding and decoding TSF headers without TSF Consul](https://github.com/Tencent/spring-cloud-tencent/pull/1816) 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 394ffa886..08e1d0dc6 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 @@ -21,6 +21,7 @@ import java.util.List; import com.tencent.cloud.common.async.PolarisAsyncProperties; import com.tencent.cloud.common.constant.OrderConstant; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.metadata.core.DecodeTransferMetadataReactiveFilter; import com.tencent.cloud.metadata.core.DecodeTransferMetadataServletFilter; import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignEnhancedPlugin; @@ -69,8 +70,9 @@ public class MetadataTransferAutoConfiguration { } @Bean - public DecodeTransferMetadataServletFilter metadataServletFilter(PolarisAsyncProperties polarisAsyncProperties) { - return new DecodeTransferMetadataServletFilter(polarisAsyncProperties); + public DecodeTransferMetadataServletFilter metadataServletFilter(PolarisAsyncProperties polarisAsyncProperties, + @Autowired(required = false) MetadataLocalProperties metadataLocalProperties) { + return new DecodeTransferMetadataServletFilter(polarisAsyncProperties, metadataLocalProperties); } } @@ -82,8 +84,9 @@ public class MetadataTransferAutoConfiguration { protected static class MetadataReactiveFilterConfig { @Bean - public DecodeTransferMetadataReactiveFilter metadataReactiveFilter(PolarisAsyncProperties polarisAsyncProperties) { - return new DecodeTransferMetadataReactiveFilter(polarisAsyncProperties); + public DecodeTransferMetadataReactiveFilter metadataReactiveFilter(PolarisAsyncProperties polarisAsyncProperties, + @Autowired(required = false) MetadataLocalProperties metadataLocalProperties) { + return new DecodeTransferMetadataReactiveFilter(polarisAsyncProperties, metadataLocalProperties); } } @@ -97,8 +100,9 @@ public class MetadataTransferAutoConfiguration { @Bean public EncodeTransferMedataScgEnhancedPlugin encodeTransferMedataScgEnhancedPlugin( - @Autowired(required = false) List contextToHeaderInterceptorList) { - return new EncodeTransferMedataScgEnhancedPlugin(contextToHeaderInterceptorList); + @Autowired(required = false) List contextToHeaderInterceptorList, + @Autowired(required = false) MetadataLocalProperties metadataLocalProperties) { + return new EncodeTransferMedataScgEnhancedPlugin(contextToHeaderInterceptorList, metadataLocalProperties); } } @@ -112,8 +116,9 @@ public class MetadataTransferAutoConfiguration { @Bean public EncodeTransferMedataFeignEnhancedPlugin encodeTransferMedataFeignEnhancedPlugin( - @Autowired(required = false) List contextToHeaderInterceptorList) { - return new EncodeTransferMedataFeignEnhancedPlugin(contextToHeaderInterceptorList); + @Autowired(required = false) List contextToHeaderInterceptorList, + @Autowired(required = false) MetadataLocalProperties metadataLocalProperties) { + return new EncodeTransferMedataFeignEnhancedPlugin(contextToHeaderInterceptorList, metadataLocalProperties); } } @@ -127,8 +132,9 @@ public class MetadataTransferAutoConfiguration { @Bean public EncodeTransferMedataRestTemplateEnhancedPlugin encodeTransferMedataRestTemplateEnhancedPlugin( - @Autowired(required = false) List contextToHeaderInterceptorList) { - return new EncodeTransferMedataRestTemplateEnhancedPlugin(contextToHeaderInterceptorList); + @Autowired(required = false) List contextToHeaderInterceptorList, + @Autowired(required = false) MetadataLocalProperties metadataLocalProperties) { + return new EncodeTransferMedataRestTemplateEnhancedPlugin(contextToHeaderInterceptorList, metadataLocalProperties); } } @@ -142,8 +148,9 @@ public class MetadataTransferAutoConfiguration { @Bean public EncodeTransferMedataWebClientEnhancedPlugin encodeTransferMedataWebClientEnhancedPlugin( - @Autowired(required = false) List contextToHeaderInterceptorList) { - return new EncodeTransferMedataWebClientEnhancedPlugin(contextToHeaderInterceptorList); + @Autowired(required = false) List contextToHeaderInterceptorList, + @Autowired(required = false) MetadataLocalProperties metadataLocalProperties) { + return new EncodeTransferMedataWebClientEnhancedPlugin(contextToHeaderInterceptorList, metadataLocalProperties); } } } 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 e903f9d1c..a22bd0043 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 @@ -26,6 +26,8 @@ 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.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.tsf.TsfContextUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.TsfTagUtils; import com.tencent.cloud.common.util.UrlUtils; @@ -59,8 +61,12 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered private final PolarisAsyncProperties polarisAsyncProperties; - public DecodeTransferMetadataReactiveFilter(PolarisAsyncProperties polarisAsyncProperties) { + private final MetadataLocalProperties metadataLocalProperties; + + public DecodeTransferMetadataReactiveFilter(PolarisAsyncProperties polarisAsyncProperties, + MetadataLocalProperties metadataLocalProperties) { this.polarisAsyncProperties = polarisAsyncProperties; + this.metadataLocalProperties = metadataLocalProperties; } @Override @@ -84,7 +90,8 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered mergedApplicationMetadata, addHeaders, callerIp, serverHttpRequest.getHeaders().getFirst(MetadataConstant.HeaderName.TSF_TAGS), serverHttpRequest.getHeaders().getFirst(MetadataConstant.HeaderName.TSF_SYSTEM_TAG), - serverHttpRequest.getHeaders().getFirst(MetadataConstant.HeaderName.TSF_METADATA)); + serverHttpRequest.getHeaders().getFirst(MetadataConstant.HeaderName.TSF_METADATA), + TsfContextUtils.isTsfHeaderCompatible(metadataLocalProperties)); // transitive metadata // from specific header 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 71e86d638..6c381abf8 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 @@ -26,6 +26,8 @@ import com.tencent.cloud.common.async.PolarisAsyncProperties; 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.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.tsf.TsfContextUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.TsfTagUtils; import com.tencent.cloud.common.util.UrlUtils; @@ -60,8 +62,12 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter { private final PolarisAsyncProperties polarisAsyncProperties; - public DecodeTransferMetadataServletFilter(PolarisAsyncProperties polarisAsyncProperties) { + private final MetadataLocalProperties metadataLocalProperties; + + public DecodeTransferMetadataServletFilter(PolarisAsyncProperties polarisAsyncProperties, + MetadataLocalProperties metadataLocalProperties) { this.polarisAsyncProperties = polarisAsyncProperties; + this.metadataLocalProperties = metadataLocalProperties; } @Override @@ -79,7 +85,8 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter { mergedApplicationMetadata, addHeaders, callerIp, httpServletRequest.getHeader(MetadataConstant.HeaderName.TSF_TAGS), httpServletRequest.getHeader(MetadataConstant.HeaderName.TSF_SYSTEM_TAG), - httpServletRequest.getHeader(MetadataConstant.HeaderName.TSF_METADATA)); + httpServletRequest.getHeader(MetadataConstant.HeaderName.TSF_METADATA), + TsfContextUtils.isTsfHeaderCompatible(metadataLocalProperties)); // transitive metadata // from specific header 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 index 05c8791f6..958658587 100644 --- 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 @@ -27,6 +27,7 @@ import java.util.Optional; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.tsf.TsfContextUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.ReflectionUtils; @@ -57,8 +58,16 @@ public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin { private List contextToHeaderInterceptorList; + private MetadataLocalProperties metadataLocalProperties; + public EncodeTransferMedataFeignEnhancedPlugin(List contextToHeaderInterceptorList) { + this(contextToHeaderInterceptorList, null); + } + + public EncodeTransferMedataFeignEnhancedPlugin(List contextToHeaderInterceptorList, + MetadataLocalProperties metadataLocalProperties) { this.contextToHeaderInterceptorList = Optional.ofNullable(contextToHeaderInterceptorList).orElse(Collections.EMPTY_LIST); + this.metadataLocalProperties = metadataLocalProperties; } @Override @@ -83,7 +92,7 @@ public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin { MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false); Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); - if (TsfContextUtils.isTsfConsulEnabled()) { + if (TsfContextUtils.isTsfHeaderCompatible(metadataLocalProperties)) { Map tsfMetadataMap = TsfTagUtils.getTsfMetadataMap(calleeTransitiveHeaders, disposableMetadata, customMetadata, applicationMetadata); this.buildHeaderMap(request, tsfMetadataMap); } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java index df7ac6667..60f23a9d8 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java @@ -24,6 +24,7 @@ import java.util.Optional; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.tsf.TsfContextUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.TsfTagUtils; @@ -53,8 +54,16 @@ public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedP private List contextToHeaderInterceptorList; + private MetadataLocalProperties metadataLocalProperties; + public EncodeTransferMedataRestTemplateEnhancedPlugin(List contextToHeaderInterceptorList) { + this(contextToHeaderInterceptorList, null); + } + + public EncodeTransferMedataRestTemplateEnhancedPlugin(List contextToHeaderInterceptorList, + MetadataLocalProperties metadataLocalProperties) { this.contextToHeaderInterceptorList = Optional.ofNullable(contextToHeaderInterceptorList).orElse(Collections.EMPTY_LIST); + this.metadataLocalProperties = metadataLocalProperties; } @Override @@ -81,7 +90,7 @@ public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedP MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false); Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); - if (TsfContextUtils.isTsfConsulEnabled()) { + if (TsfContextUtils.isTsfHeaderCompatible(metadataLocalProperties)) { Map tsfMetadataMap = TsfTagUtils.getTsfMetadataMap(calleeTransitiveHeaders, disposableMetadata, customMetadata, applicationMetadata); this.buildHeaderMap(httpRequest, tsfMetadataMap); } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java index 1374a686e..43eadc9e9 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java @@ -25,6 +25,7 @@ import java.util.Optional; import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.tsf.TsfContextUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.TsfTagUtils; @@ -55,8 +56,16 @@ public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin { private List contextToHeaderInterceptorList; + private MetadataLocalProperties metadataLocalProperties; + public EncodeTransferMedataScgEnhancedPlugin(List contextToHeaderInterceptorList) { + this(contextToHeaderInterceptorList, null); + } + + public EncodeTransferMedataScgEnhancedPlugin(List contextToHeaderInterceptorList, + MetadataLocalProperties metadataLocalProperties) { this.contextToHeaderInterceptorList = Optional.ofNullable(contextToHeaderInterceptorList).orElse(Collections.EMPTY_LIST); + this.metadataLocalProperties = metadataLocalProperties; } @Override @@ -88,7 +97,7 @@ public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin { MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false); Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); - if (TsfContextUtils.isTsfConsulEnabled()) { + if (TsfContextUtils.isTsfHeaderCompatible(metadataLocalProperties)) { this.buildHeaderMap(builder, TsfTagUtils.getTsfMetadataMap(calleeTransitiveHeaders, disposableMetadata, customMetadata, applicationMetadata)); } // currently only support transitive header from calleeMessageMetadataContainer diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java index e31eeffb0..dca061bb6 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java @@ -24,6 +24,7 @@ import java.util.Optional; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.tsf.TsfContextUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.TsfTagUtils; @@ -53,8 +54,16 @@ public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlug private List contextToHeaderInterceptorList; + private MetadataLocalProperties metadataLocalProperties; + public EncodeTransferMedataWebClientEnhancedPlugin(List contextToHeaderInterceptorList) { + this(contextToHeaderInterceptorList, null); + } + + public EncodeTransferMedataWebClientEnhancedPlugin(List contextToHeaderInterceptorList, + MetadataLocalProperties metadataLocalProperties) { this.contextToHeaderInterceptorList = Optional.ofNullable(contextToHeaderInterceptorList).orElse(Collections.EMPTY_LIST); + this.metadataLocalProperties = metadataLocalProperties; } @Override @@ -80,7 +89,7 @@ public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlug Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); ClientRequest.Builder requestBuilder = ClientRequest.from(clientRequest); - if (TsfContextUtils.isTsfConsulEnabled()) { + if (TsfContextUtils.isTsfHeaderCompatible(metadataLocalProperties)) { this.buildHeaderMap(requestBuilder, TsfTagUtils.getTsfMetadataMap(calleeTransitiveHeaders, disposableMetadata, customMetadata, applicationMetadata)); } // currently only support transitive header from calleeMessageMetadataContainer 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 19460df0f..ae08a77ad 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 @@ -58,7 +58,7 @@ public class DecodeTransferMetadataReactiveFilterTest { @BeforeEach public void setUp() { - this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(polarisAsyncProperties); + this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(polarisAsyncProperties, metadataLocalProperties); } @Test diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferTsfHeaderCompatibleTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferTsfHeaderCompatibleTest.java new file mode 100644 index 000000000..a97ff0691 --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferTsfHeaderCompatibleTest.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. 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.IOException; +import java.util.concurrent.atomic.AtomicReference; + +import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.util.UrlUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * Test TSF header decode when Consul is disabled and tsf-header-compatible is true. + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, + classes = DecodeTransferTsfHeaderCompatibleTest.TestApplication.class, + properties = {"spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = servlet", + "spring.cloud.gateway.enabled = false", + "spring.cloud.tencent.metadata.tsf-header-compatible = true"}) +public class DecodeTransferTsfHeaderCompatibleTest { + + @Autowired + private DecodeTransferMetadataServletFilter metadataServletFilter; + + @Test + public void testDecodeTsfTagsWithoutConsul() throws ServletException, IOException { + AtomicReference featValue = new AtomicReference<>(); + FilterChain filterChain = (servletRequest, servletResponse) -> + featValue.set(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "feat")); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(MetadataConstant.HeaderName.TSF_TAGS, + UrlUtils.encode("[{\"k\":\"feat\",\"v\":\"test\",\"f\":[\"0\"]}]")); + metadataServletFilter.doFilter(request, new MockHttpServletResponse(), filterChain); + + assertThat(featValue.get()).isEqualTo("test"); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferTsfHeaderCompatibleTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferTsfHeaderCompatibleTest.java new file mode 100644 index 000000000..bbbd3d2e8 --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferTsfHeaderCompatibleTest.java @@ -0,0 +1,115 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. 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.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.tsf.TsfContextUtils; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import feign.Request; +import feign.RequestTemplate; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test TSF header encode when Consul is disabled. + */ +public class EncodeTransferTsfHeaderCompatibleTest { + + @BeforeEach + @AfterEach + public void reset() throws Exception { + MetadataContextHolder.remove(); + Field isTsfConsulEnabledFirst = TsfContextUtils.class.getDeclaredField("isTsfConsulEnabledFirstConfiguration"); + isTsfConsulEnabledFirst.setAccessible(true); + ((AtomicBoolean) isTsfConsulEnabledFirst.get(null)).set(true); + + Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled"); + tsfConsulEnabledField.setAccessible(true); + tsfConsulEnabledField.set(null, false); + } + + @Test + public void testEncodeTsfHeaderWhenCompatibleWithoutConsul() throws Throwable { + MetadataContextHolder.get().setTransitiveMetadata(Collections.singletonMap("feat", "test")); + MetadataLocalProperties properties = new MetadataLocalProperties(); + properties.setTsfHeaderCompatible(true); + EncodeTransferMedataFeignEnhancedPlugin plugin = + new EncodeTransferMedataFeignEnhancedPlugin(new ArrayList<>(), properties); + + Request request = createRequest(); + EnhancedPluginContext context = new EnhancedPluginContext(); + context.setOriginRequest(request); + plugin.run(context); + + assertThat(request.headers()).containsKey(MetadataConstant.HeaderName.TSF_TAGS); + } + + @Test + public void testSkipTsfHeaderWhenIncompatibleWithoutConsul() throws Throwable { + MetadataContextHolder.get().setTransitiveMetadata(Collections.singletonMap("feat", "test")); + MetadataLocalProperties properties = new MetadataLocalProperties(); + properties.setTsfHeaderCompatible(false); + EncodeTransferMedataFeignEnhancedPlugin plugin = + new EncodeTransferMedataFeignEnhancedPlugin(new ArrayList<>(), properties); + + Request request = createRequest(); + EnhancedPluginContext context = new EnhancedPluginContext(); + context.setOriginRequest(request); + plugin.run(context); + + assertThat(request.headers()).doesNotContainKey(MetadataConstant.HeaderName.TSF_TAGS); + assertThat(request.headers()).containsKey(MetadataConstant.HeaderName.CUSTOM_METADATA); + } + + @Test + public void testEncodeTsfHeaderWhenConsulEnabled() throws Throwable { + Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled"); + tsfConsulEnabledField.setAccessible(true); + tsfConsulEnabledField.set(null, true); + + MetadataContextHolder.get().setTransitiveMetadata(Collections.singletonMap("feat", "test")); + EncodeTransferMedataFeignEnhancedPlugin plugin = + new EncodeTransferMedataFeignEnhancedPlugin(new ArrayList<>(), new MetadataLocalProperties()); + + Request request = createRequest(); + EnhancedPluginContext context = new EnhancedPluginContext(); + context.setOriginRequest(request); + plugin.run(context); + + assertThat(request.headers()).containsKey(MetadataConstant.HeaderName.TSF_TAGS); + } + + private Request createRequest() { + Map> headers = Collections.emptyMap(); + return Request.create(Request.HttpMethod.GET, "http://localhost/test", + headers, new byte[0], StandardCharsets.UTF_8, new RequestTemplate()); + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataLocalProperties.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataLocalProperties.java index 5ae0af141..554317ffe 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataLocalProperties.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataLocalProperties.java @@ -53,6 +53,12 @@ public class MetadataLocalProperties { */ private List headers; + /** + * Whether to encode/decode TSF-Tags headers when TSF Consul is not enabled. + * Default false. + */ + private boolean tsfHeaderCompatible; + public Map getContent() { if (CollectionUtils.isEmpty(content)) { content = new HashMap<>(); @@ -97,6 +103,14 @@ public class MetadataLocalProperties { this.headers = headers; } + public boolean isTsfHeaderCompatible() { + return tsfHeaderCompatible; + } + + public void setTsfHeaderCompatible(boolean tsfHeaderCompatible) { + this.tsfHeaderCompatible = tsfHeaderCompatible; + } + @Override public String toString() { return "MetadataLocalProperties{" + @@ -104,6 +118,7 @@ public class MetadataLocalProperties { ", transitive=" + transitive + ", disposable=" + disposable + ", headers=" + headers + + ", tsfHeaderCompatible=" + tsfHeaderCompatible + '}'; } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/tsf/TsfContextUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/tsf/TsfContextUtils.java index 3821fcd24..ba12b0578 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/tsf/TsfContextUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/tsf/TsfContextUtils.java @@ -19,6 +19,7 @@ package com.tencent.cloud.common.tsf; import java.util.concurrent.atomic.AtomicBoolean; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.polaris.api.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,8 +91,31 @@ public final class TsfContextUtils { * This method should be called after {@link com.tencent.cloud.common.tsf.TsfContextUtils#isTsfConsulEnabled(Environment)}. * @return whether Tsf Consul is enabled */ - public static boolean isTsfConsulEnabled() { return tsfConsulEnabled; } + + /** + * Whether TSF HTTP headers should be encoded or decoded. + *

True when TSF Consul is enabled, or when + * {@code spring.cloud.tencent.metadata.tsf-header-compatible} is true. + * @param configuredTsfHeaderCompatible configured tsf-header-compatible value + * @return whether TSF headers should be processed + */ + public static boolean isTsfHeaderCompatible(boolean configuredTsfHeaderCompatible) { + return configuredTsfHeaderCompatible || isTsfConsulEnabled(); + } + + /** + * Whether TSF HTTP headers should be encoded or decoded. + *

True when TSF Consul is enabled, or when + * {@code spring.cloud.tencent.metadata.tsf-header-compatible} is true. + * @param metadataLocalProperties local metadata properties, may be {@code null} + * @return whether TSF headers should be processed + */ + public static boolean isTsfHeaderCompatible(MetadataLocalProperties metadataLocalProperties) { + boolean configured = metadataLocalProperties != null + && metadataLocalProperties.isTsfHeaderCompatible(); + return isTsfHeaderCompatible(configured); + } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/TsfTagUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/TsfTagUtils.java index 63b64f681..461371180 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/TsfTagUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/TsfTagUtils.java @@ -167,9 +167,10 @@ public final class TsfTagUtils { public static void updateTsfMetadata(Map mergedTransitiveMetadata, Map mergedDisposableMetadata, Map mergedApplicationMetadata, Map addHeaders, - AtomicReference callerIp, String encodedUserTagList, String encodedSystemTagList, String encodedMetadata) { + AtomicReference callerIp, String encodedUserTagList, String encodedSystemTagList, String encodedMetadata, + boolean tsfHeaderCompatible) { - if (!TsfContextUtils.isTsfConsulEnabled()) { + if (!TsfContextUtils.isTsfHeaderCompatible(tsfHeaderCompatible)) { return; } List tsfUserTagList = TsfTagUtils.deserializeTagList(encodedUserTagList); diff --git a/spring-cloud-tencent-commons/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-tencent-commons/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 9df3e2d80..d34c27732 100644 --- a/spring-cloud-tencent-commons/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-tencent-commons/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -20,6 +20,12 @@ "type": "java.util.List", "description": "Custom transitive http header key list." }, + { + "name": "spring.cloud.tencent.metadata.tsf-header-compatible", + "type": "java.lang.Boolean", + "defaultValue": false, + "description": "Encode and decode TSF-Tags headers when TSF Consul is not enabled. Default: false." + }, { "name": "spring.cloud.tencent.async.enabled", "type": "java.lang.Boolean", diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java index c3c935a38..f71ee9a1e 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java @@ -59,6 +59,11 @@ public class MetadataLocalPropertiesTest { assertThat(metadataLocalProperties.getHeaders().contains("d")).isTrue(); } + @Test + public void testTsfHeaderCompatibleDefault() { + assertThat(metadataLocalProperties.isTsfHeaderCompatible()).isFalse(); + } + @SpringBootApplication protected static class TestApplication { diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTsfHeaderCompatibleTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTsfHeaderCompatibleTest.java new file mode 100644 index 000000000..068527bda --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTsfHeaderCompatibleTest.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. 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.common.metadata.config; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test binding of tsf-header-compatible. + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = MetadataLocalPropertiesTsfHeaderCompatibleTest.TestApplication.class, + properties = {"spring.config.location = classpath:application-test.yml", + "spring.cloud.tencent.metadata.tsf-header-compatible=true"}) +public class MetadataLocalPropertiesTsfHeaderCompatibleTest { + + @Autowired + private MetadataLocalProperties metadataLocalProperties; + + @Test + public void testTsfHeaderCompatibleBinding() { + assertThat(metadataLocalProperties.isTsfHeaderCompatible()).isTrue(); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/tsf/TsfContextUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/tsf/TsfContextUtilsTest.java new file mode 100644 index 000000000..3efb05ca8 --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/tsf/TsfContextUtilsTest.java @@ -0,0 +1,78 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. 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.common.tsf; + +import java.lang.reflect.Field; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link TsfContextUtils}. + */ +public class TsfContextUtilsTest { + + @BeforeEach + @AfterEach + public void reset() throws Exception { + Field isTsfConsulEnabledFirst = TsfContextUtils.class.getDeclaredField("isTsfConsulEnabledFirstConfiguration"); + isTsfConsulEnabledFirst.setAccessible(true); + ((AtomicBoolean) isTsfConsulEnabledFirst.get(null)).set(true); + + Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled"); + tsfConsulEnabledField.setAccessible(true); + tsfConsulEnabledField.set(null, false); + } + + @Test + public void testIsTsfHeaderCompatibleWhenConfigured() { + assertThat(TsfContextUtils.isTsfHeaderCompatible(true)).isTrue(); + assertThat(TsfContextUtils.isTsfConsulEnabled()).isFalse(); + } + + @Test + public void testIsTsfHeaderCompatibleWhenConsulEnabled() throws Exception { + Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled"); + tsfConsulEnabledField.setAccessible(true); + tsfConsulEnabledField.set(null, true); + + assertThat(TsfContextUtils.isTsfHeaderCompatible(false)).isTrue(); + } + + @Test + public void testIsTsfHeaderCompatibleWhenDisabled() { + assertThat(TsfContextUtils.isTsfHeaderCompatible(false)).isFalse(); + } + + @Test + public void testIsTsfHeaderCompatibleFromProperties() { + assertThat(TsfContextUtils.isTsfHeaderCompatible((MetadataLocalProperties) null)) + .isFalse(); + + MetadataLocalProperties properties = new MetadataLocalProperties(); + assertThat(TsfContextUtils.isTsfHeaderCompatible(properties)).isFalse(); + + properties.setTsfHeaderCompatible(true); + assertThat(TsfContextUtils.isTsfHeaderCompatible(properties)).isTrue(); + } +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/TsfTagUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/TsfTagUtilsTest.java index d2230469d..d7424d361 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/TsfTagUtilsTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/TsfTagUtilsTest.java @@ -17,7 +17,10 @@ package com.tencent.cloud.common.util; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; @@ -79,4 +82,52 @@ public class TsfTagUtilsTest { assertThat(TsfTagUtils.deserializeMetadata(null)).isNull(); assertThat(TsfTagUtils.deserializeMetadata("")).isNull(); } + + @Test + public void testUpdateTsfMetadataWhenCompatible() { + Map transitive = new HashMap<>(); + Map disposable = new HashMap<>(); + Map application = new HashMap<>(); + Map addHeaders = new HashMap<>(); + AtomicReference callerIp = new AtomicReference<>(""); + String encodedTags = UrlUtils.encode("[{\"k\":\"feat\",\"v\":\"test\",\"f\":[\"0\"]}]"); + + TsfTagUtils.updateTsfMetadata(transitive, disposable, application, addHeaders, callerIp, + encodedTags, null, null, true); + + assertThat(transitive).containsEntry("feat", "test"); + assertThat(disposable).isEmpty(); + } + + @Test + public void testUpdateTsfMetadataWhenIncompatible() { + Map transitive = new HashMap<>(); + Map disposable = new HashMap<>(); + Map application = new HashMap<>(); + Map addHeaders = new HashMap<>(); + AtomicReference callerIp = new AtomicReference<>(""); + String encodedTags = UrlUtils.encode("[{\"k\":\"feat\",\"v\":\"test\",\"f\":[\"0\"]}]"); + + TsfTagUtils.updateTsfMetadata(transitive, disposable, application, addHeaders, callerIp, + encodedTags, null, null, false); + + assertThat(transitive).isEmpty(); + assertThat(disposable).isEmpty(); + } + + @Test + public void testUpdateTsfMetadataDisposableWhenCompatible() { + Map transitive = new HashMap<>(); + Map disposable = new HashMap<>(); + Map application = new HashMap<>(); + Map addHeaders = new HashMap<>(); + AtomicReference callerIp = new AtomicReference<>(""); + String encodedTags = UrlUtils.encode("[{\"k\":\"operation\",\"v\":\"rest\",\"f\":[]}]"); + + TsfTagUtils.updateTsfMetadata(transitive, disposable, application, addHeaders, callerIp, + encodedTags, null, null, true); + + assertThat(transitive).isEmpty(); + assertThat(disposable).containsEntry("operation", "rest"); + } } diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/controller/MetadataInspectController.java b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/controller/MetadataInspectController.java new file mode 100644 index 000000000..fa6e29edd --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/controller/MetadataInspectController.java @@ -0,0 +1,116 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. 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.tsf.demo.consumer.controller; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.tsf.demo.consumer.proxy.ProviderDemoService; +import com.tencent.polaris.api.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.tsf.core.TsfContext; +import org.springframework.tsf.core.entity.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * Put TSF tags then dump local context or call provider /metadata. + */ +@RestController +public class MetadataInspectController { + + private static final Logger LOG = LoggerFactory.getLogger(MetadataInspectController.class); + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private ProviderDemoService providerDemoService; + + @GetMapping("/local-metadata") + public Map localMetadata( + @RequestParam(required = false) String hopKey, + @RequestParam(required = false) String hopValue, + @RequestParam(required = false) String passKey, + @RequestParam(required = false) String passValue) { + LOG.info("GET /local-metadata hop={}:{} pass={}:{}", hopKey, hopValue, passKey, passValue); + putTags(hopKey, hopValue, passKey, passValue); + Map body = dumpLocal(); + LOG.info("GET /local-metadata dump after putTag (no outbound): {}", body); + return body; + } + + @GetMapping("/echo-metadata-rest") + @SuppressWarnings("unchecked") + public Map echoMetadataRest( + @RequestParam(required = false) String hopKey, + @RequestParam(required = false) String hopValue, + @RequestParam(required = false) String passKey, + @RequestParam(required = false) String passValue) { + LOG.info("GET /echo-metadata-rest hop={}:{} pass={}:{}, RestTemplate -> provider-demo /metadata", + hopKey, hopValue, passKey, passValue); + putTags(hopKey, hopValue, passKey, passValue); + Map body = restTemplate.getForObject("http://provider-demo/metadata", Map.class); + LOG.info("GET /echo-metadata-rest provider /metadata response: {}", body); + return body; + } + + @GetMapping("/echo-metadata-feign") + @SuppressWarnings("unchecked") + public Map echoMetadataFeign( + @RequestParam(required = false) String hopKey, + @RequestParam(required = false) String hopValue, + @RequestParam(required = false) String passKey, + @RequestParam(required = false) String passValue) { + LOG.info("GET /echo-metadata-feign hop={}:{} pass={}:{}, " + + "Feign ProviderDemoService#metadata() -> provider-demo /metadata", + hopKey, hopValue, passKey, passValue); + putTags(hopKey, hopValue, passKey, passValue); + Map body = providerDemoService.metadata(); + LOG.info("GET /echo-metadata-feign provider /metadata response: {}", body); + return body; + } + + private void putTags(String hopKey, String hopValue, String passKey, String passValue) { + if (StringUtils.isNotBlank(hopKey)) { + TsfContext.putTag(hopKey, hopValue); + LOG.info("TsfContext.putTag one-hop (no flag) {}:{}", hopKey, hopValue); + } + if (StringUtils.isNotBlank(passKey)) { + TsfContext.putTags(Collections.singletonMap(passKey, passValue), Tag.ControlFlag.TRANSITIVE); + LOG.info("TsfContext.putTags TRANSITIVE {}:{}", passKey, passValue); + } + } + + private Map dumpLocal() { + MetadataContext ctx = MetadataContextHolder.get(); + Map body = new LinkedHashMap<>(); + body.put("calleeTransitive", ctx.getTransitiveMetadata()); + body.put("calleeDisposable", ctx.getDisposableMetadata()); + body.put("callerDisposable", ctx.getFragmentContext(MetadataContext.FRAGMENT_UPSTREAM_DISPOSABLE)); + return body; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java index a0659795f..dc944bd20 100644 --- a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java @@ -17,6 +17,8 @@ package com.tencent.cloud.tsf.demo.consumer.proxy; +import java.util.Map; + import com.tencent.cloud.tsf.demo.consumer.entity.User; import org.springframework.cloud.openfeign.FeignClient; @@ -39,4 +41,7 @@ public interface ProviderDemoService { @RequestMapping(value = "/user", method = RequestMethod.POST) String user(@RequestBody User user); + + @RequestMapping(value = "/metadata", method = RequestMethod.GET) + Map metadata(); } diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/bootstrap.yml index 7236c2952..6c4f0c7b0 100644 --- a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/bootstrap.yml @@ -8,6 +8,13 @@ feign: tsf: enabled: true +# 连接北极星且需要编解码 TSF-Tags 时打开(默认 false,有 tsf_consul_ip 时无需打开) +#spring: +# cloud: +# tencent: +# metadata: +# tsf-header-compatible: true + #本地测试时打开 #tsf_namespace_id: default_namespace diff --git a/spring-cloud-tencent-examples/tsf-example/msgw-scg/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/tsf-example/msgw-scg/src/main/resources/bootstrap.yml index 6f4b1c4c2..bdcd63c04 100644 --- a/spring-cloud-tencent-examples/tsf-example/msgw-scg/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/tsf-example/msgw-scg/src/main/resources/bootstrap.yml @@ -18,6 +18,10 @@ spring: consul: enabled: true scheme: HTTP + tencent: + metadata: + # 连接北极星且需要编解码 TSF-Tags 时设为 true(默认 false,有 tsf_consul_ip 时无需打开) + # tsf-header-compatible: true logging: level: diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/MetadataInspectController.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/MetadataInspectController.java new file mode 100644 index 000000000..6f6eb2063 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/MetadataInspectController.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. 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.tsf.demo.provider; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Dump incoming TSF/SCT headers and MetadataContext buckets for tag verification. + */ +@RestController +public class MetadataInspectController { + + private static final Logger LOG = LoggerFactory.getLogger(MetadataInspectController.class); + + @GetMapping("/metadata") + public Map metadata(HttpServletRequest request) { + MetadataContext ctx = MetadataContextHolder.get(); + Map body = new LinkedHashMap<>(); + Map headers = new LinkedHashMap<>(); + headers.put(MetadataConstant.HeaderName.TSF_TAGS, + request.getHeader(MetadataConstant.HeaderName.TSF_TAGS)); + headers.put(MetadataConstant.HeaderName.TSF_SYSTEM_TAG, + request.getHeader(MetadataConstant.HeaderName.TSF_SYSTEM_TAG)); + headers.put(MetadataConstant.HeaderName.TSF_METADATA, + request.getHeader(MetadataConstant.HeaderName.TSF_METADATA)); + headers.put(MetadataConstant.HeaderName.CUSTOM_METADATA, + request.getHeader(MetadataConstant.HeaderName.CUSTOM_METADATA)); + headers.put(MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA, + request.getHeader(MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA)); + body.put("headers", headers); + body.put("calleeTransitive", ctx.getTransitiveMetadata()); + body.put("calleeDisposable", ctx.getDisposableMetadata()); + body.put("callerDisposable", ctx.getFragmentContext(MetadataContext.FRAGMENT_UPSTREAM_DISPOSABLE)); + LOG.info("GET /metadata incoming headers TSF-Tags={} TSF-System-Tags={} TSF-Metadata={} " + + "SCT-CUSTOM-METADATA={} SCT-CUSTOM-DISPOSABLE-METADATA={}", + headers.get(MetadataConstant.HeaderName.TSF_TAGS), + headers.get(MetadataConstant.HeaderName.TSF_SYSTEM_TAG), + headers.get(MetadataConstant.HeaderName.TSF_METADATA), + headers.get(MetadataConstant.HeaderName.CUSTOM_METADATA), + headers.get(MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA)); + LOG.info("GET /metadata MetadataContext calleeTransitive={} calleeDisposable={} callerDisposable={}", + body.get("calleeTransitive"), body.get("calleeDisposable"), body.get("callerDisposable")); + return body; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/bootstrap.yml index 08650f511..5237a71d1 100644 --- a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/bootstrap.yml @@ -17,6 +17,9 @@ spring: rpc-enhancement: reporter: enabled: true + metadata: + # 连接北极星且需要编解码 TSF-Tags 时设为 true(默认 false,有 tsf_consul_ip 时无需打开) + # tsf-header-compatible: true logging: file: