0

I'd like to limit the allowed length of the bodies of incoming HTTP POST requests in my Spring-based Java server application.

Most of these requests contain payloads with content type application/json. However, some of them have other content types, e.g. some controllers receive application/octet-stream from a messaging service.

So far, I had been working with a javax.servlet.Filter, that inspects the content length and rejects any overlong requests:

@Component
@WebFilter(urlPatterns = "my-path/*")
public class Limiter implements Filter {

    private static final int MAX_CONTENT_LENGTH_IN_BYTES = 5000;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        int length = servletRequest.getContentLength();
        if (length > MAX_CONTENT_LENGTH_IN_BYTES) {
            // reject request with HTTP 400 Bad Request
        } else {
            // let the request pass
        }

}

While this works fine for JSON-based contents, it fails for some of the others, esp. the octet-streams. For these cases, getContentLength() consistently returns -1. From the Java documentation, I gather that this indicates that the length is unavailable from the input stream.

What's a more reliable way to inspect the content length? Clone the stream, then retrieve it as a string and check the string's length?

Similar questions like Increase HTTP Post maxPostSize in Spring Boot were answered by pointing at settings like spring.servlet.multipart.max-request-size. We tried all of these and they do not work in our case. These settings are designed for multipart data transfer, where the payload is distributed over multiple requests that are then reassembled by the recipient; they do not apply to single requests. Note also that in the existing questions, users would like to increase the allowed request size. Increasing means they already have a size limit in place that works. In our case, we do not have a working configuration, but want to create one.

Florian
  • 4,821
  • 2
  • 19
  • 44
  • 1
    You dont have to "clone" the stream, just make it rewindable and you can always inspect the buffer length. getContentLength will be rather only when client will send it as header - and this might not be accurate anyway. – Antoniossss Apr 25 '22 at 07:29

2 Answers2

0

This may be done directly in the configuration file of your Spring application, e.g in application.yml .

The following would limit sizes to 1Gb.

spring:
  http:
    multipart:
      max-file-size: 1000MB
      max-request-size: 1000MB
Arnaud
  • 17,229
  • 3
  • 31
  • 44
  • Doesn't this affect multipart requests only? What if my request is single part? – Florian Apr 25 '22 at 07:34
  • Finally got around to proofing this. It does not work. Spring considers multipart settings only for multipart requests. Single-part requests are not limited by this. – Florian Oct 26 '22 at 13:06
0

You can use Tomcat's settings to do that

spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=1MB

That is obivously for embedded Tomcat if you are using SpringBoot. Otherwise you will have to do such configuration on connector settings which can be found in Tomcat documentation

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • Doesn't this affect multipart requests only? What if my request is single part? – Florian Apr 25 '22 at 07:34
  • Probably not. https://stackoverflow.com/a/33726892/1527544 – Antoniossss Apr 25 '22 at 07:36
  • Finally got around to proofing this. It does not work. Spring considers multipart settings only for multipart requests. Single-part requests are not limited by this. – Florian Oct 26 '22 at 13:06
  • @Florian please share the solution – Prudhvik Chirunomula Nov 25 '22 at 18:58
  • @PrudhvikChirunomula I cannot share my solution because the question is closed as a duplicate. The question that the closing argument relates to however has a different intent and providing my answer there does not fit. ‍♂️ – Florian Dec 01 '22 at 09:02
  • 1
    I edited the question and asked to reopen it to be able to provide the answer. In short, our answer is: Wrap the incoming request in a custom HttpServletRequestWrapper and pass that down the filter chain. Let it wrap the original SeverletInputStream with a custom SizeLimitedInputStream class that counts the calls to .read(), and accepts n calls, then throws an exception on the n+1th call. – Florian Dec 01 '22 at 09:46