Friday, May 4, 2018

Spring Boot: 'Why' & 'How to' Configure maxSwallowSize property for embedded Tomcat

Hello Friends! Today we would be discussing why in first place, anyone needs to configure maxSwallowSize property and what are the steps that needs to be followed in order to configure this property. Can this property be directly configured by Spring Boot's application.yml / application.properties file or any Java configuration is required for this?



Table Of Content
1. Introduction
2. Why configure maxSwallowSize property
3. How to configure maxSwallowSize property
4. Conclusion


1. Introduction


At first, let's understand what does 'maxSwallowSize' property signify?
maxSwallowSize : From Apache Tomcat 8 Configuration Reference documentation[1]:
"The maximum number of request body bytes (excluding transfer encoding overhead) that will be swallowed by Tomcat for an aborted upload. An aborted upload is when Tomcat knows that the request body is going to be ignored but the client still sends it. If Tomcat does not swallow the body, the client is unlikely to see the response. If not specified the default of 2097152 (2 megabytes) will be used. A value of less than zero indicates that no limit should be enforced.
maxSwallowSize is a property of the connector. Although the swallow size isn't specific to file uploads but generally in case file uploads , the request size tend be larger as compared to normal requests. Therefore for file uploads, one might need to configure this property otherwise Tomcat may terminate the connection when user tries to upload files having size larger than the maxSwallowSize threshold and user would not be able to see the meaningful message about what just happened with the application.

Currently maxSwallowSize property can't be configured by Spring Boot's configuration file i.e. application.yml / application.properties [2]. But it might come up as the future enhancement. For now, Java configuration is required for this.

2. Why configure maxSwallowSize property?


There is one reason for configuring maxSwallowSize property which is described below:

To avoid the Connection Reset issue while file uploads: 

When users tries to upload files whose size exceeds the maxSwallowSize property value then Tomcat terminate the connection and user would see the ERR_CONNECTION_RESET error in the browser. Even if, user have their Controller Advice Interceptor class[3] in place for catching the exception, it will never be invoked.  Therefore one need to configure maxSwallowSize property for embedded Tomcat.

3. How to configure maxSwallowSize property?


Since maxSwallowSize is a property of a Connector. We need to use the TomcatConnectorCustomizer functional interface for setting the maxSwallowSize property and then associate it with the TomcatEmbeddedServletContainerFactory class. One can use any one of the below code snippet in their main Spring Boot application class.
  1. Using simple java
    #application.properties 
    maxswallowsize.inmb = -1
    or
    #application.yml 
    maxswallowsize:
      inmb: -1
    
    import org.apache.coyote.http11.AbstractHttp11Protocol;
    import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
    import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
    
    @Value("${maxswallowsize.inmb}")
    private int maxSwallowSizeInMb;
    
    @Bean
    public TomcatEmbeddedServletContainerFactory containerFactory() {
    
     TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
            
        TomcatConnectorCustomizer tomcatConnectorCustomizer = new TomcatConnectorCustomizer() {
      @Override
      public void customize(Connector connector) {
       if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol)) {
        //  -1 means, accept unlimited bytes, get this value from application.yml using @Value
        ((AbstractHttp11Protocol) connector.getProtocolHandler()).setMaxSwallowSize(maxSwallowSizeInMb);
              }
       }
        };
            
     containerFactory.addConnectorCustomizers(tomcatConnectorCustomizer);
        return containerFactory;
    }
    
  2. Using Java Lambda Expression
    @Bean
    public TomcatEmbeddedServletContainerFactory containerFactory() {
    
     TomcatEmbeddedServletContainerFactory tomcatContainerFactory = new TomcatEmbeddedServletContainerFactory();
    
        tomcatContainerFactory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
      if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol)) {
                    ((AbstractHttp11Protocol) connector.getProtocolHandler()).setMaxSwallowSize(-1);
             }
     });
    
            return tomcatContainerFactory;
    }
    
Note: We shouldn't configure maxSwallowSize property value to -1 for production environment. Suppose, your application have multiple file upload forms with different max upload file size value configured then we should configure maxSwallowSize property value to max upload file size value of the upload form whose limit is maximum.
For example: Upload Form 1 [max upload file size = 10MB], Upload Form 2 [max upload file size = 20MB] & Upload Form 3 [max upload file size = 30MB] then maxSwallowSize property value  = max(10MB,20MB,30MB) = 30MB.

4. Conclusion

Now we know, 'Why' & 'How to' configure the maxSwallowSize property value for the embedded Tomcat. Therefore, if one have properly configured maxSwallowSize property value & a Controller Advice Interceptor class then one could avoid the connection reset issue and display a meaningful, user friendly validation message to the user for any file upload related Exception.

Appendix:

[1]: The standard HTTP connectors attributes
[2]: Common Spring Boot application properties
[3]: Class annotated with @ControllerAdvice is required for catching the file upload related Exception. Our controller can't catch these type of exception as the request is rejected for these type of exception and control never goes to Controller method in debug mode.


No comments:

Post a Comment