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

pull/641/head
Haotian Zhang 2 years ago committed by GitHub
parent 9a0d199e2e
commit fb4d270073
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,6 +4,9 @@
- [Optimize: remove discovery module useless code](https://github.com/Tencent/spring-cloud-tencent/pull/596)
- [Optimize: optimize configuration conditional & optimize config data tips"](https://github.com/Tencent/spring-cloud-tencent/pull/604)
- [Optimize: Maybe remove Chinese characters](https://github.com/Tencent/spring-cloud-tencent/pull/608)
- [Optimize: InstanceId of PolarisRegistration and PolarisServiceRegistry](https://github.com/Tencent/spring-cloud-tencent/pull/611)
- [Bugfix: fix feign report call result error when using feign direct call](https://github.com/Tencent/spring-cloud-tencent/pull/622)
- [remove useless code for router](https://github.com/Tencent/spring-cloud-tencent/pull/625)
- [Feature: support spring-retry router](https://github.com/Tencent/spring-cloud-tencent/pull/633)
- [Bugfix: fix throw npe when router context is null](https://github.com/Tencent/spring-cloud-tencent/pull/634)
- [feat:Transfer http headers specified by environment variables](https://github.com/Tencent/spring-cloud-tencent/pull/637)

@ -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
@ -79,12 +86,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.ReactiveLoadBalancerClientFilter.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}

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

@ -26,8 +26,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();

@ -13,7 +13,6 @@
* 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;
@ -35,7 +34,7 @@ import org.springframework.util.CollectionUtils;
/**
* manage metadata from env/config file/custom spi.
*
* @author lepdou 2022-05-20
* @author lepdou, Haotian Zhang
*/
public class StaticMetadataManager {
/**
@ -55,12 +54,19 @@ 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_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;
@ -94,14 +100,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);
}
@ -269,6 +284,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