聊聊spring|聊聊spring cloud gateway的RemoveHopByHopHeadersFilter

序 本文主要研究一下spring cloud gateway的RemoveHopByHopHeadersFilter
GatewayAutoConfiguration 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
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 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 headers = HEADERS_REMOVED_ON_REQUEST; public Set getHeaders() { return headers; }public void setHeaders(Set 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); } }

【聊聊spring|聊聊spring cloud gateway的RemoveHopByHopHeadersFilter】可以看到这个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" }, { "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 headersFilters; public NettyRoutingFilter(HttpClient httpClient, ObjectProvider headersFilters) { this.httpClient = httpClient; this.headersFilters = headersFilters; }@Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; }@Override public Mono 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。
doc
  • 112.9 RemoveNonProxyHeaders GatewayFilter Factory

    推荐阅读