过滤器 PreserveHostHeader · SpringCloud微服务实战 · 看云

4. 过滤器 PreserveHostHeader

导航

PreserveHostHeader GatewayFilter Factory没有参数。 此过滤器设置路由过滤器将检查的请求属性,以确定是否应发送原始主机头,而不是http客户端确定的主机头

本节代码地址


4.1 应用配置

server:
  port: 8699
spring:
  application:
    name: fw-gateways-gateway
  profiles:
    active: preserve_host_route
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
---
spring:
  cloud:
    gateway:
      routes:
        - id: preserve_host_route  
          uri: lb://fw-cloud-ribbon-server
          predicates:
          - After=2020-01-08T18:30:11.965+08:00[Asia/Shanghai]
          filters:
          - PreserveHostHeader
  profiles: preserve_host_route

4.2 PreserveHostHeaderGatewayFilterFactory 源码

这个filter就往exchange添加PRESERVE_HOST_HEADER_ATTRIBUTE,设置为true

public class PreserveHostHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory {

   public GatewayFilter apply() {
      return apply(o -> {
      });
   }

   public GatewayFilter apply(Object config) {
      return new GatewayFilter() {
         @Override
         public Mono<Void> filter(ServerWebExchange exchange,
               GatewayFilterChain chain) {
            exchange.getAttributes().put(PRESERVE_HOST_HEADER_ATTRIBUTE, true);
            return chain.filter(exchange);
         }

         @Override
         public String toString() {
            return filterToStringCreator(PreserveHostHeaderGatewayFilterFactory.this)
                  .toString();
         }
      };
   }

}

4.3 NettyRoutingFilter 源码

NettyRoutingFilter 是最后真正发起http请求的

@Override
@SuppressWarnings("Duplicates")
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.getMethodValue());
   final String url = requestUrl.toASCIIString();

   HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);

   final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
   filtered.forEach(httpHeaders::set);

   boolean preserveHost = exchange
         .getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);

   Flux<HttpClientResponse> responseFlux = this.httpClient.headers(headers -> {
      headers.add(httpHeaders);
      if (preserveHost) {
         String host = request.getHeaders().getFirst(HttpHeaders.HOST);
         headers.add(HttpHeaders.HOST, host);
      }
      else {
         
         headers.remove(HttpHeaders.HOST);
      }
   }).request(method).uri(url).send((req, nettyOutbound) -> {
      if (log.isTraceEnabled()) {
         nettyOutbound.withConnection(connection -> log.trace(
               "outbound route: " + connection.channel().id().asShortText()
                     + ", inbound: " + exchange.getLogPrefix()));
      }
      return nettyOutbound.send(request.getBody()
            .map(dataBuffer -> ((NettyDataBuffer) dataBuffer).getNativeBuffer()));
   }).responseConnection((res, connection) -> {

      
      
      
      exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
      exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);

      ServerHttpResponse response = exchange.getResponse();
      
      HttpHeaders headers = new HttpHeaders();

      res.responseHeaders()
            .forEach(entry -> headers.add(entry.getKey(), entry.getValue()));

      String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
      if (StringUtils.hasLength(contentTypeValue)) {
         exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,
               contentTypeValue);
      }

      HttpStatus status = HttpStatus.resolve(res.status().code());
      if (status != null) {
         response.setStatusCode(status);
      }
      else if (response instanceof AbstractServerHttpResponse) {
         
         ((AbstractServerHttpResponse) response)
               .setStatusCodeValue(res.status().code());
      }
      else {
         
         throw new IllegalStateException("Unable to set status code on response: "
               + res.status().code() + ", " + response.getClass());
      }

      
      
      HttpHeaders filteredResponseHeaders = HttpHeadersFilter
            .filter(getHeadersFilters(), headers, exchange, Type.RESPONSE);

      if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)
            && filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {
         
         
         
         
         response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
      }

      exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES,
            filteredResponseHeaders.keySet());

      response.getHeaders().putAll(filteredResponseHeaders);

      return Mono.just(res);
   });

   if (properties.getResponseTimeout() != null) {
      responseFlux = responseFlux.timeout(properties.getResponseTimeout(),
            Mono.error(new TimeoutException("Response took longer than timeout: "
                  + properties.getResponseTimeout())))
            .onErrorMap(TimeoutException.class,
                  th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,
                        th.getMessage(), th));
   }

   return responseFlux.then(chain.filter(exchange));
}

02a216ebda3260ffe82fbdce7a5de0ad_MD5.png

不过,配置了不是有http 确认的Host ,可能会使网关报400错误
5061ad5809624dc91261a943bb265585_MD5.png

我们把配置- PreserveHostHeader去掉,发现默认是false,然后会把header 中的Host 取消掉,转发到服务的时候Header 就是网关服务的了。
3248e93485ae06d4d8e822aa464c980f_MD5.png