Search code examples
spring-cloudnetflix-zuulspring-cloud-netflixspring-cloud-gateway

spring cloud gateway, Is Request size limit filter is available


I am working in a project with spring-cloud-gateway. I see that the Request Size limitation filter is yet not available. But I need to develop it. Any idea , is it coming ? or should I start my own development.

I Know that it is difficult to get any answer, as except the developers there are a few persons who are working on it.


Solution

  • I have created a filter named RequestSizeGatewayFilterFactory, It is working fine for our application as of now. But not sure this can be a part of the spring-cloud-gateway project.

    package com.api.gateway.somename.filter;
    
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.util.Assert;
    import org.springframework.util.StringUtils;
    
    /**
     * This filter blocks the request, if the request size is more than
     * the permissible size.The default request size is 5 MB
     *
     * @author Arpan
     */
    public class RequestSizeGatewayFilterFactory
            extends AbstractGatewayFilterFactory<RequestSizeGatewayFilterFactory.RequestSizeConfig> {
    
        private static String PREFIX = "kMGTPE";
        private static String ERROR = "Request size is larger than permissible limit." +
                "Request size is %s where permissible limit is %s";
    
        public RequestSizeGatewayFilterFactory() {
            super(RequestSizeGatewayFilterFactory.RequestSizeConfig.class);
        }
    
        @Override
        public GatewayFilter apply(RequestSizeGatewayFilterFactory.RequestSizeConfig requestSizeConfig) {
            requestSizeConfig.validate();
            return (exchange, chain) -> {
                ServerHttpRequest request = exchange.getRequest();
                String contentLength = request.getHeaders().getFirst("content-length");
                if (!StringUtils.isEmpty(contentLength)) {
                    Long currentRequestSize = Long.valueOf(contentLength);
                    if (currentRequestSize > requestSizeConfig.getMaxSize()) {
                        exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
                        exchange.getResponse().getHeaders().add("errorMessage",
                                getErrorMessage(currentRequestSize, requestSizeConfig.getMaxSize()));
                        return exchange.getResponse().setComplete();
                    }
                }
                return chain.filter(exchange);
            };
        }
    
        public static class RequestSizeConfig {
    
            // 5 MB is the default request size
            private Long maxSize = 5000000L;
    
            public RequestSizeGatewayFilterFactory.RequestSizeConfig setMaxSize(Long maxSize) {
                this.maxSize = maxSize;
                return this;
            }
    
            public Long getMaxSize() {
                return maxSize;
            }
    
            public void validate() {
                Assert.isTrue(this.maxSize != null && this.maxSize > 0,
                        "maxSize must be greater than 0");
                Assert.isInstanceOf(Long.class, maxSize, "maxSize must be a number");
            }
        }
    
        private static String getErrorMessage(Long currentRequestSize, Long maxSize) {
            return String.format(ERROR,
                    getHumanReadableByteCount(currentRequestSize),
                    getHumanReadableByteCount(maxSize));
        }
    
        private static String getHumanReadableByteCount(long bytes) {
            int unit = 1000;
            if (bytes < unit) return bytes + " B";
            int exp = (int) (Math.log(bytes) / Math.log(unit));
            String pre = Character.toString(PREFIX.charAt(exp - 1));
            return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
        }
    }
    

    And the configuration for the filter is: When it works as a default filter:

    spring:
      application:
        name: somename
      cloud:
        gateway:
          default-filters:
          - Hystrix=default
          - RequestSize=7000000
    

    When needs to be applied in some API

    # ===========================================
      - id: request_size_route
        uri: ${test.uri}/upload
        predicates:
        - Path=/upload
        filters:
        - name: RequestSize
          args:
            maxSize: 5000000
    

    Also you need to configure the bean in some component scanable class in your project, which is GatewayAutoConfiguration for the spring-cloud-gateway-core project.

    @Bean
    public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() {
       return new RequestSizeGatewayFilterFactory();
    }