zalando/logbook

Invalid `Content-Length` of incoming request causes to java.util.concurrent.TimeoutException: Idle timeout expired

maistrovyi opened this issue · 1 comments

if you send Content-Length header value longer than request body actually is - java.util.concurrent.TimeoutException: Idle timeout expired will be thrown.

Description

It is clear that this is initially the client's problem if they send an invalid Content-Length and of course, it's clear that when we tell the server I'm sending you 10 bytes but send only 6, it expects 4 more until the end of the timeout, but the problem appears only if there is a Logbook dependency (which is logical due to buffering request), without it everything works fine.

So maybe the problem here is more conceptual, like we need to support new behavior for the new org.zalando.logbook.servlet.FormRequestMode?

Expected Behavior

Request handled normally

Actual Behavior

java.io.IOException: java.util.concurrent.TimeoutException: Idle timeout expired: 30005/30000 ms
	at org.eclipse.jetty.ee10.servlet.HttpInput.read(HttpInput.java:267)
	at org.eclipse.jetty.ee10.servlet.HttpInput.read(HttpInput.java:225)
	at java.base/java.io.InputStream.read(InputStream.java:220)
	at org.zalando.logbook.servlet.ByteStreams.copy(ByteStreams.java:27)
	at org.zalando.logbook.servlet.ByteStreams.toByteArray(ByteStreams.java:17)
	at org.zalando.logbook.servlet.RemoteRequest$Offering.buffer(RemoteRequest.java:110)
	at org.zalando.logbook.servlet.RemoteRequest.lambda$buffer$0(RemoteRequest.java:309)
	at org.zalando.fauxpas.ThrowingFunction.apply(ThrowingFunction.java:19)
	at java.base/java.util.concurrent.atomic.AtomicReference.updateAndGet(AtomicReference.java:210)
	at org.zalando.logbook.servlet.RemoteRequest.buffer(RemoteRequest.java:308)
	at org.zalando.logbook.servlet.RemoteRequest.getBody(RemoteRequest.java:286)
	at org.zalando.logbook.HttpMessage.getBodyAsString(HttpMessage.java:44)
    ...

Possible Fix

Check content-length and compare with request.getInputStream().available()?

Steps to Reproduce

  1. Create some servlet controller like:
@RestController
@RequestMapping(value = "/api/v1/test")
public class TestController {

    @PostMapping
    String echo(@RequestBody String body) {
        return "echo";
    }
}
  1. Make curl with invalid Content-Length header like:
curl --location 'http://localhost:8080/api/v1/test' --request POST \
--header 'Content-Type: application/json' \
--header 'Content-Length: 584' \
--data 'some-string'

Context

After Logbook introducing - the client's requests started to fall.

Your Environment

2.7.* spring boot and 2.16 logbook, Jetty/Tomcat
3.2.* spring boot and 3.9 logbook, Jetty/Tomcat