美文网首页Spring Cloud
聊聊spring cloud gateway的RemoveHop

聊聊spring cloud gateway的RemoveHop

作者: go4it | 来源:发表于2018-05-30 22:44 被阅读135次

    本文主要研究一下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<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。

    doc

    相关文章

      网友评论

        本文标题:聊聊spring cloud gateway的RemoveHop

        本文链接:https://www.haomeiwen.com/subject/hlqfsftx.html