feat:Transfer http headers specified by environment variables. (#638)

pull/649/head
Haotian Zhang 2 years ago committed by GitHub
parent ecd97c33c7
commit 1dd2a5a4b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,9 +2,12 @@
---
- [Optimize: remove discovery module useless code](https://github.com/Tencent/spring-cloud-tencent/pull/597)
- [Bugfix: InstancePreRegisteredEvent and InstanceRegisteredEvent modify Registration info](https://github.com/Tencent/spring-cloud-tencent/pull/583)
- [Fix issue 579:Report the labels in request when report the result of invocation by RestTemplate](https://github.com/Tencent/spring-cloud-tencent/pull/600)
- [Optimize: optimize configuration conditional & optimize config data tips"](https://github.com/Tencent/spring-cloud-tencent/pull/605)
- [Optimize: Maybe remove Chinese characters](https://github.com/Tencent/spring-cloud-tencent/pull/609)
- [Optimize: InstanceId of PolarisRegistration and PolarisServiceRegistry](https://github.com/Tencent/spring-cloud-tencent/pull/612)
- [Bugfix: fix feign report call result error when using feign direct call](https://github.com/Tencent/spring-cloud-tencent/pull/623)
- [optimize:optimize PolarisRouterContext and constants.](https://github.com/Tencent/spring-cloud-tencent/pull/628)
- [support spring-retry router](https://github.com/Tencent/spring-cloud-tencent/pull/631)
- [feat:Transfer http headers specified by environment variables](https://github.com/Tencent/spring-cloud-tencent/pull/638)

@ -20,8 +20,12 @@ 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.Set;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
@ -33,6 +37,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 +46,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
@ -80,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<String, String> transHeaderMetadata = MetadataContextHolder.get()
.getFragmentContext(FRAGMENT_RAW_TRANSHEADERS);
if (!CollectionUtils.isEmpty(transHeaderMetadata)) {
String transHeaders = transHeaderMetadata.keySet().stream().findFirst().orElse("");
String[] transHeaderArray = transHeaders.split(",");
HttpHeaders headers = serverHttpRequest.getHeaders();
Set<String> headerKeys = headers.keySet();
Iterator<String> iterator = headerKeys.iterator();
while (iterator.hasNext()) {
String httpHeader = iterator.next();
Arrays.stream(transHeaderArray).forEach(transHeader -> {
if (transHeader.equals(httpHeader)) {
List<String> list = headers.get(httpHeader);
String httpHeaderValue = JacksonUtils.serialize2Json(list);
MetadataContextHolder.get()
.putContext(FRAGMENT_RAW_TRANSHEADERS_KV, httpHeader, httpHeaderValue);
}
});
}
}
}
private Map<String, String> getIntervalMetadata(ServerHttpRequest serverHttpRequest, String headerName) {
HttpHeaders httpHeaders = serverHttpRequest.getHeaders();
String customMetadataStr = httpHeaders.getFirst(headerName);

@ -21,6 +21,8 @@ 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;
@ -37,12 +39,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 +76,36 @@ 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<String, String> transHeaderMetadata = MetadataContextHolder.get()
.getFragmentContext(FRAGMENT_RAW_TRANSHEADERS);
if (!CollectionUtils.isEmpty(transHeaderMetadata)) {
String transHeaders = transHeaderMetadata.keySet().stream().findFirst().orElse("");
String[] transHeaderArray = transHeaders.split(",");
Enumeration<String> httpHeaders = httpServletRequest.getHeaderNames();
while (httpHeaders.hasMoreElements()) {
String httpHeader = httpHeaders.nextElement();
Arrays.stream(transHeaderArray).forEach(transHeader -> {
if (transHeader.equals(httpHeader)) {
String httpHeaderValue = httpServletRequest.getHeader(httpHeader);
// for example, {"trans-headers-kv" : {"header1":"v1","header2":"v2"...}}
MetadataContextHolder.get()
.putContext(FRAGMENT_RAW_TRANSHEADERS_KV, httpHeader, httpHeaderValue);
}
});
}
}
}
private Map<String, String> getInternalMetadata(HttpServletRequest httpServletRequest, String headerName) {
// Get custom metadata string from http header.
String customMetadataStr = httpServletRequest.getHeader(headerName);

@ -61,6 +61,7 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor,
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
Map<String, String> disposableMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE);
Map<String, String> transHeaders = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV);
// Clean up one-time metadata coming from upstream .
Map<String, String> 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<String, String> transHeaders) {
if (!CollectionUtils.isEmpty(transHeaders)) {
transHeaders.entrySet().stream().forEach(entry -> {
requestTemplate.removeHeader(entry.getKey());
requestTemplate.header(entry.getKey(), entry.getValue());
});
}
}
/**

@ -61,6 +61,7 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
Map<String, String> disposableMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE);
Map<String, String> transHeaders = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV);
Map<String, String> 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<String, String> 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} .
*

@ -20,8 +20,12 @@ 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.Set;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
@ -32,6 +36,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 +44,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.LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
/**
@ -80,9 +87,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<String, String> transHeaderMetadata = MetadataContextHolder.get()
.getFragmentContext(FRAGMENT_RAW_TRANSHEADERS);
if (!CollectionUtils.isEmpty(transHeaderMetadata)) {
String transHeaders = transHeaderMetadata.keySet().stream().findFirst().orElse("");
String[] transHeaderArray = transHeaders.split(",");
HttpHeaders headers = serverHttpRequest.getHeaders();
Set<String> headerKeys = headers.keySet();
Iterator<String> iterator = headerKeys.iterator();
while (iterator.hasNext()) {
String httpHeader = iterator.next();
Arrays.stream(transHeaderArray).forEach(transHeader -> {
if (transHeader.equals(httpHeader)) {
List<String> 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);
}
});
}
}
}
/**
* Set metadata into the request header for {@link ServerHttpRequest.Builder} .
* @param builder instance of {@link ServerHttpRequest.Builder}

@ -20,6 +20,8 @@ package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
@ -34,6 +36,8 @@ 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;
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.netflix.zuul.filters.support.FilterConstants.RIBBON_ROUTING_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ROUTE_TYPE;
@ -83,9 +87,36 @@ public class EncodeTransferMetadataZuulFilter extends ZuulFilter {
this.buildMetadataHeader(requestContext, newestCustomMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(requestContext, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
setCompleteTransHeaderIntoMC(requestContext);
return null;
}
/**
* 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(RequestContext requestContext) {
// transHeaderMetadata: for example, {"trans-headers" : {"header1,header2,header3":""}}
Map<String, String> transHeaderMetadata = MetadataContextHolder.get()
.getFragmentContext(FRAGMENT_RAW_TRANSHEADERS);
if (!CollectionUtils.isEmpty(transHeaderMetadata)) {
String transHeaders = transHeaderMetadata.keySet().stream().findFirst().orElse("");
String[] transHeaderArray = transHeaders.split(",");
Enumeration<String> httpHeaders = requestContext.getRequest().getHeaderNames();
while (httpHeaders.hasMoreElements()) {
String httpHeader = httpHeaders.nextElement();
Arrays.stream(transHeaderArray).forEach(transHeader -> {
if (transHeader.equals(httpHeader)) {
String httpHeaderValue = requestContext.getRequest().getHeader(httpHeader);
// for example, {"trans-headers-kv" : {"header1":"v1","header2":"v2"...}}
MetadataContextHolder.get()
.putContext(FRAGMENT_RAW_TRANSHEADERS_KV, httpHeader, httpHeaderValue);
}
});
}
}
}
/**
* Set metadata into the request header for {@link RequestContext} .
*

@ -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.

@ -27,8 +27,10 @@ import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
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;
@ -68,6 +70,10 @@ public final class MetadataContextHolder {
metadataContext.putFragmentContext(FRAGMENT_TRANSITIVE, staticMetadataManager.getMergedStaticTransitiveMetadata());
metadataContext.putFragmentContext(FRAGMENT_DISPOSABLE, staticMetadataManager.getMergedStaticDisposableMetadata());
if (StringUtils.hasText(staticMetadataManager.getTransHeaderFromEnv())) {
metadataContext.putContext(FRAGMENT_RAW_TRANSHEADERS, staticMetadataManager.getTransHeaderFromEnv(), "");
}
METADATA_CONTEXT.set(metadataContext);
return METADATA_CONTEXT.get();

@ -56,12 +56,19 @@ public class StaticMetadataManager {
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_TRAFFIC_CONTENT_RAW_TRANSHEADERS = "SCT_TRAFFIC_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";
private Map<String, String> envMetadata;
private Map<String, String> envTransitiveMetadata;
private Map<String, String> envDisposableMetadata;
private Map<String, String> envNotReportMetadata;
private Map<String, String> configMetadata;
private Map<String, String> configTransitiveMetadata;
private Map<String, String> configDisposableMetadata;
@ -95,14 +102,23 @@ public class StaticMetadataManager {
Map<String, String> allEnvs = System.getenv();
envMetadata = new HashMap<>();
envNotReportMetadata = new HashMap<>();
// parse all metadata
for (Map.Entry<String, String> entry : allEnvs.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (StringUtils.isNotBlank(key) && key.startsWith(ENV_METADATA_PREFIX)
if (StringUtils.isNotBlank(key)
&& (key.startsWith(ENV_METADATA_PREFIX) || key.equals(ENV_TRAFFIC_CONTENT_RAW_TRANSHEADERS))
&& !key.equals(ENV_METADATA_CONTENT_TRANSITIVE)) {
String sourceKey = StringUtils.substring(key, ENV_METADATA_PREFIX_LENGTH);
envMetadata.put(sourceKey, value);
String sourceKey;
if (key.equals(ENV_TRAFFIC_CONTENT_RAW_TRANSHEADERS)) {
sourceKey = key;
envNotReportMetadata.put(sourceKey, value);
}
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 +286,10 @@ public class StaticMetadataManager {
return envMetadata;
}
public String getTransHeaderFromEnv() {
return envNotReportMetadata.get(ENV_TRAFFIC_CONTENT_RAW_TRANSHEADERS);
}
public Map<String, String> getEnvTransitiveMetadata() {
return envTransitiveMetadata;
}

Loading…
Cancel
Save