spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.7k stars 38.14k forks source link

SseEmitter (ResponseBodyEmitter) keeps collecting messages if not initialized #25442

Closed kgcsabi closed 4 years ago

kgcsabi commented 4 years ago

Details

Component: spring-webmvc Affects: 5.2.7.RELEASE Impact: Memory leak

Description

The ResponseBodyEmitter instance is normally initialized by the framework upon returned by the RestController method. The messages that are passed in the meantime are collected in a LinkedHashSet.

When the connection is dropped by the client early enough then the initialization will never be invoked.

These instances keep collecting the messages with no limitation. To make matters worse, these instances are not evicted when timeout is reached.

This may result in OutOfMemoryError on the long run.

Steps to reproduce:

  1. Start Server app in debug mode
  2. Add breakpoint to org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler:129 //emitter.initialize(handler);
  3. Start Client app
  4. When breakpoint hit then stop Client app

Attachments

Ideas

In my opinion this may be addressed by

archiezgg commented 4 years ago

+1

rstoyanchev commented 4 years ago

I can't reproduce the exact scenario. The ISE "Async request already returned to container" from the logs is under WebAsyncRequest#startDeferredResultProcessing which is a couple of lines before emitter.initialize(handler). That means the connection must have been closed by then. I tried dropping the connection at that stage but I still can't get startDeferredResultProcessing to raise the same error.

In any case it's clear that we need to protect against very early connection issues.