本文主要研究一下spring cloud gateway的RemoveHopByHopHeadersFilter
spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
//......
@Bean
public RemoveHopByHopHeadersFilter removeHopByHopHeadersFilter() {
return new RemoveHopByHopHeadersFilter();
}
//......
}
可以看到这里自动new了一个RemoveHopByHopHeadersFilter
spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java
@ConfigurationProperties("spring.cloud.gateway.filter.remove-hop-by-hop")
public class RemoveHopByHopHeadersFilter implements HttpHeadersFilter, Ordered {
public static final Set<String> HEADERS_REMOVED_ON_REQUEST =
new HashSet<>(Arrays.asList(
"connection",
"keep-alive",
"transfer-encoding",
"te",
"trailer",
"proxy-authorization",
"proxy-authenticate",
"x-application-context",
"upgrade"
// these two are not listed in https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3
//"proxy-connection",
// "content-length",
));
private int order = Ordered.LOWEST_PRECEDENCE;
private Set<String> headers = HEADERS_REMOVED_ON_REQUEST;
public Set<String> getHeaders() {
return headers;
}
public void setHeaders(Set<String> headers) {
this.headers = headers;
}
@Override
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) {
HttpHeaders filtered = new HttpHeaders();
input.entrySet().stream()
.filter(entry -> !this.headers.contains(entry.getKey().toLowerCase()))
.forEach(entry -> filtered.addAll(entry.getKey(), entry.getValue()));
return filtered;
}
@Override
public boolean supports(Type type) {
return type.equals(Type.REQUEST) ||
type.equals(Type.RESPONSE);
}
}
可以看到这个filter是移除request或response中指定的header,默认移除的header包括”connection”,”keep-alive”, “transfer-encoding”,”te”,”trailer”,”proxy-authorization”,”proxy-authenticate”,”x-application-context”,”upgrade”。 也可以自己在配置文件指定要移除的header
spring-cloud-gateway-core-2.0.0.RC1.jar!/META-INF/spring-configuration-metadata.json
{
"sourceType": "org.springframework.cloud.gateway.filter.headers.RemoveHopByHopHeadersFilter",
"name": "spring.cloud.gateway.filter.remove-hop-by-hop.headers",
"type": "java.util.Set<java.lang.String>"
},
{
"sourceType": "org.springframework.cloud.gateway.filter.headers.RemoveHopByHopHeadersFilter",
"name": "spring.cloud.gateway.filter.remove-hop-by-hop.order",
"type": "java.lang.Integer"
}
可以看到,有个order属性用来指定该filter的优先级,默认是Ordered.LOWEST_PRECEDENCE 还有另外一个属性headers,用来指定要移除的header
实例
spring:
cloud:
gateway:
filter:
remove-hop-by-hop:
headers:
- x-route
- x-auth-id
spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java
public class NettyRoutingFilter implements GlobalFilter, Ordered {
private final HttpClient httpClient;
private final ObjectProvider<List<HttpHeadersFilter>> headersFilters;
public NettyRoutingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
this.httpClient = httpClient;
this.headersFilters = headersFilters;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) {
return chain.filter(exchange);
}
setAlreadyRouted(exchange);
ServerHttpRequest request = exchange.getRequest();
final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString());
final String url = requestUrl.toString();
HttpHeaders filtered = filterRequest(this.headersFilters.getIfAvailable(),
exchange);
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
filtered.forEach(httpHeaders::set);
String transferEncoding = request.getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING);
boolean chunkedTransfer = "chunked".equalsIgnoreCase(transferEncoding);
boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
return this.httpClient.request(method, url, req -> {
final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
.headers(httpHeaders)
.chunkedTransfer(chunkedTransfer)
.failOnServerError(false)
.failOnClientError(false);
if (preserveHost) {
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
proxyRequest.header(HttpHeaders.HOST, host);
}
return proxyRequest.sendHeaders() //I shouldn't need this
.send(request.getBody().map(dataBuffer ->
((NettyDataBuffer)dataBuffer).getNativeBuffer()));
}).doOnNext(res -> {
ServerHttpResponse response = exchange.getResponse();
// put headers and status so filters can modify the response
HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE);
response.getHeaders().putAll(filteredResponseHeaders);
HttpStatus status = HttpStatus.resolve(res.status().code());
if (status != null) {
response.setStatusCode(status);
} else if (response instanceof AbstractServerHttpResponse) {
// https://jira.spring.io/browse/SPR-16748
((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code());
} else {
throw new IllegalStateException("Unable to set status code on response: " +res.status().code()+", "+response.getClass());
}
// Defer committing the response until all route filters have run
// Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
}).then(chain.filter(exchange));
}
}
这里头对于request,调用的是
HttpHeaders filtered = filterRequest(this.headersFilters.getIfAvailable(),
exchange);
对于response调用的是
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE);
RemoveHopByHopHeadersFilter可以用来移除指定的header,其作用于request及response。