diff --git a/CHANGELOG.md b/CHANGELOG.md index 59f42e40..f879f31d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,4 +5,5 @@ - [Optimize: remove discovery module useless code](https://github.com/Tencent/spring-cloud-tencent/pull/595) - [Optimize: remove useless code for rest template router](https://github.com/Tencent/spring-cloud-tencent/pull/601) - [Optimize: optimize configuration conditional & optimize config data tips](https://github.com/Tencent/spring-cloud-tencent/pull/603) +- [Automatically transmit some headers specified by a environment varaible that directly defines header keys.](https://github.com/Tencent/spring-cloud-tencent/pull/606) - [Optimize: Maybe remove Chinese characters](https://github.com/Tencent/spring-cloud-tencent/pull/607) 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 957493ca..104af37b 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 @@ -20,8 +20,13 @@ package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.metadata.MetadataContextHolder; @@ -33,6 +38,7 @@ 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.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; @@ -41,6 +47,8 @@ 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; +import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_RAW_TRANSHEADERS; +import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV; /** * Filter used for storing the metadata from upstream temporarily when web application is @@ -79,12 +87,41 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered MetadataConstant.HeaderName.METADATA_CONTEXT, MetadataContextHolder.get()); + setCompleteTransHeaderIntoMC(serverHttpRequest); return webFilterChain.filter(serverWebExchange) .doOnError(throwable -> LOG.error("handle metadata[{}] error.", MetadataContextHolder.get(), throwable)) .doFinally((type) -> MetadataContextHolder.remove()); } + /** + * According to ServerHttpRequest and trans-headers(key list in string type) in metadata, build + * the complete headers(key-value list in map type) into metadata. + */ + private void setCompleteTransHeaderIntoMC(ServerHttpRequest serverHttpRequest) { + // transHeaderMetadata: for example, {"trans-headers" : {"header1;header2;header3":""}} + Map transHeaderMetadata = MetadataContextHolder.get() + .getFragmentContext(FRAGMENT_RAW_TRANSHEADERS); + if (!CollectionUtils.isEmpty(transHeaderMetadata)) { + Optional transHeaders = transHeaderMetadata.keySet().stream().findFirst(); + String[] transHeaderArray = transHeaders.get().split(";"); + HttpHeaders headers = serverHttpRequest.getHeaders(); + Set headerKeys = headers.keySet(); + Iterator iterator = headerKeys.iterator(); + while (iterator.hasNext()) { + String httpHeader = iterator.next(); + Arrays.stream(transHeaderArray).forEach(transHeader -> { + if (transHeader.equals(httpHeader)) { + List list = headers.get(httpHeader); + String httpHeaderValue = JacksonUtils.serialize2Json(list); + MetadataContextHolder.get().putContext(FRAGMENT_RAW_TRANSHEADERS_KV, httpHeader, httpHeaderValue); + return; + } + }); + } + } + } + private Map getIntervalMetadata(ServerHttpRequest serverHttpRequest, String headerName) { HttpHeaders httpHeaders = serverHttpRequest.getHeaders(); String customMetadataStr = httpHeaders.getFirst(headerName); 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 a6756391..cdf1f8e4 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 @@ -21,8 +21,11 @@ package com.tencent.cloud.metadata.core; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.util.Arrays; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -37,12 +40,15 @@ import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.lang.NonNull; +import org.springframework.util.CollectionUtils; 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; +import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_RAW_TRANSHEADERS; +import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV; /** * Filter used for storing the metadata from upstream temporarily when web application is @@ -71,9 +77,35 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter { MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata); + setCompleteTransHeaderIntoMC(httpServletRequest); filterChain.doFilter(httpServletRequest, httpServletResponse); } + /** + * According to HttpServletRequest and trans-headers(key list in string type) in metadata, build + * the complete headers(key-value list in map type) into metadata. + */ + private void setCompleteTransHeaderIntoMC(HttpServletRequest httpServletRequest) { + // transHeaderMetadata: for example, {"trans-headers" : {"header1;header2;header3":""}} + Map transHeaderMetadata = MetadataContextHolder.get() + .getFragmentContext(FRAGMENT_RAW_TRANSHEADERS); + if (!CollectionUtils.isEmpty(transHeaderMetadata)) { + Optional transHeaders = transHeaderMetadata.keySet().stream().findFirst(); + String[] transHeaderArray = transHeaders.get().split(";"); + Enumeration httpHeaders = httpServletRequest.getHeaderNames(); + while (httpHeaders.hasMoreElements()) { + String httpHeader = httpHeaders.nextElement(); + Arrays.stream(transHeaderArray).forEach(transHeader -> { + if (transHeader.equals(httpHeader)) { + String httpHeaderValue = httpServletRequest.getHeader(httpHeader); + MetadataContextHolder.get().putContext(FRAGMENT_RAW_TRANSHEADERS_KV, httpHeader, httpHeaderValue); + return; + } + }); + } + } + } + private Map getInternalMetadata(HttpServletRequest httpServletRequest, String headerName) { // Get custom metadata string from http header. String customMetadataStr = httpServletRequest.getHeader(headerName); 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 index fa63f3df..b35ec02d 100644 --- 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 @@ -61,6 +61,7 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, MetadataContext metadataContext = MetadataContextHolder.get(); Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Map disposableMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE); + Map transHeaders = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV); // Clean up one-time metadata coming from upstream . Map newestCustomMetadata = new HashMap<>(); @@ -71,8 +72,20 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, }); this.buildMetadataHeader(requestTemplate, disposableMetadata, CUSTOM_DISPOSABLE_METADATA); - // process custom metadata finally + // process custom metadata this.buildMetadataHeader(requestTemplate, newestCustomMetadata, 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()); + }); + } } /** 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/EncodeTransferMedataRestTemplateInterceptor.java index 513310da..89d5c5f6 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/EncodeTransferMedataRestTemplateInterceptor.java @@ -61,6 +61,7 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe MetadataContext metadataContext = MetadataContextHolder.get(); Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Map disposableMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE); + Map transHeaders = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV); Map newestCustomMetadata = new HashMap<>(); customMetadata.forEach((key, value) -> { @@ -74,9 +75,20 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe // build custom metadata request header this.buildMetadataHeader(httpRequest, newestCustomMetadata, CUSTOM_METADATA); + // 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) { + if (!CollectionUtils.isEmpty(transHeaders)) { + transHeaders.entrySet().stream().forEach(entry -> { + request.getHeaders().set(entry.getKey(), entry.getValue()); + }); + } + } + /** * Set metadata into the request header for {@link HttpRequest} . * 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/EncodeTransferMedataScgFilter.java index 95d7b5b2..bd6b07ab 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/EncodeTransferMedataScgFilter.java @@ -20,8 +20,13 @@ package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.metadata.MetadataContext; @@ -32,6 +37,7 @@ import reactor.core.publisher.Mono; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.CollectionUtils; import org.springframework.web.server.ServerWebExchange; @@ -39,6 +45,8 @@ 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.cloud.common.metadata.MetadataContext.FRAGMENT_RAW_TRANSHEADERS; +import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV; import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER; /** @@ -80,9 +88,39 @@ public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered { this.buildMetadataHeader(builder, newestCustomMetadata, CUSTOM_METADATA); this.buildMetadataHeader(builder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA); + setCompleteTransHeaderIntoMC(exchange.getRequest()); return chain.filter(exchange.mutate().request(builder.build()).build()); } + /** + * According to ServerHttpRequest and trans-headers(key list in string type) in metadata, build + * the complete headers(key-value list in map type) into metadata. + */ + private void setCompleteTransHeaderIntoMC(ServerHttpRequest serverHttpRequest) { + // transHeaderMetadata: for example, {"trans-headers" : {"header1;header2;header3":""}} + Map transHeaderMetadata = MetadataContextHolder.get() + .getFragmentContext(FRAGMENT_RAW_TRANSHEADERS); + if (!CollectionUtils.isEmpty(transHeaderMetadata)) { + Optional transHeaders = transHeaderMetadata.keySet().stream().findFirst(); + String[] transHeaderArray = transHeaders.get().split(";"); + HttpHeaders headers = serverHttpRequest.getHeaders(); + Set headerKeys = headers.keySet(); + Iterator iterator = headerKeys.iterator(); + while (iterator.hasNext()) { + String httpHeader = iterator.next(); + Arrays.stream(transHeaderArray).forEach(transHeader -> { + if (transHeader.equals(httpHeader)) { + List list = headers.get(httpHeader); + String httpHeaderValue = JacksonUtils.serialize2Json(list); + // for example, {"trans-headers-kv" : {"header1":"v1","header2":"v2"...}} + MetadataContextHolder.get().putContext(FRAGMENT_RAW_TRANSHEADERS_KV, httpHeader, httpHeaderValue); + return; + } + }); + } + } + } + /** * Set metadata into the request header for {@link ServerHttpRequest.Builder} . * @param builder instance of {@link ServerHttpRequest.Builder} 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 4288ab8e..d0ccaf08 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 @@ -56,6 +56,16 @@ public class MetadataContext { */ public static final String FRAGMENT_UPSTREAM_DISPOSABLE = "upstream-disposable"; + /** + * the key of the header(key) list needed to be transmitted from upstream to downstream. + */ + public static final String FRAGMENT_RAW_TRANSHEADERS = "trans-headers"; + + /** + * the key of the header(key-value) list needed to be transmitted from upstream to downstream. + */ + public static final String FRAGMENT_RAW_TRANSHEADERS_KV = "trans-headers-kv"; + private static final Logger LOG = LoggerFactory.getLogger(MetadataContext.class); /** * Namespace of local instance. 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 28520517..3fe11f38 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 @@ -28,6 +28,7 @@ import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import org.springframework.util.CollectionUtils; import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_DISPOSABLE; +import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_RAW_TRANSHEADERS; import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_TRANSITIVE; import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_UPSTREAM_DISPOSABLE; @@ -67,6 +68,7 @@ public final class MetadataContextHolder { MetadataContext metadataContext = new MetadataContext(); metadataContext.putFragmentContext(FRAGMENT_TRANSITIVE, staticMetadataManager.getMergedStaticTransitiveMetadata()); metadataContext.putFragmentContext(FRAGMENT_DISPOSABLE, staticMetadataManager.getMergedStaticDisposableMetadata()); + metadataContext.putContext(FRAGMENT_RAW_TRANSHEADERS, staticMetadataManager.getEnvTransHeaderMetadata(), ""); METADATA_CONTEXT.set(metadataContext); diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/StaticMetadataManager.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/StaticMetadataManager.java index 832e854e..b983bc90 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/StaticMetadataManager.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/StaticMetadataManager.java @@ -55,6 +55,12 @@ public class StaticMetadataManager { private static final int ENV_METADATA_PREFIX_LENGTH = ENV_METADATA_PREFIX.length(); private static final String ENV_METADATA_CONTENT_TRANSITIVE = "SCT_METADATA_CONTENT_TRANSITIVE"; private static final String ENV_METADATA_CONTENT_DISPOSABLE = "SCT_METADATA_CONTENT_DISPOSABLE"; + /** + * This is the key of the header's key list needed to be transmitted. The list is a string split with ;. + * The value mapped by this key was specified by user. + * This is configured in environment variables. + */ + private static final String ENV_METADATA_CONTENT_RAW_TRANSHEADERS = "SCT_METADATA_CONTENT_RAW_TRANSHEADERS"; private static final String ENV_METADATA_ZONE = "SCT_METADATA_ZONE"; private static final String ENV_METADATA_REGION = "SCT_METADATA_REGION"; private static final String ENV_METADATA_CAMPUS = "SCT_METADATA_CAMPUS"; @@ -100,7 +106,13 @@ public class StaticMetadataManager { String value = entry.getValue(); if (StringUtils.isNotBlank(key) && key.startsWith(ENV_METADATA_PREFIX) && !key.equals(ENV_METADATA_CONTENT_TRANSITIVE)) { - String sourceKey = StringUtils.substring(key, ENV_METADATA_PREFIX_LENGTH); + String sourceKey = ""; + if (key.equals(ENV_METADATA_CONTENT_RAW_TRANSHEADERS)) { + sourceKey = key; + } + else { + sourceKey = StringUtils.substring(key, ENV_METADATA_PREFIX_LENGTH); + } envMetadata.put(sourceKey, value); LOGGER.info("[SCT] resolve metadata from env. key = {}, value = {}", sourceKey, value); @@ -270,6 +282,10 @@ public class StaticMetadataManager { return envMetadata; } + public String getEnvTransHeaderMetadata() { + return envMetadata.get(ENV_METADATA_CONTENT_RAW_TRANSHEADERS); + } + public Map getEnvTransitiveMetadata() { return envTransitiveMetadata; }