Spring Boot
REST API
StreamingResponseBody
Async Programming
Request Timeout

RestController with StreamingResponseBody async.request-timeout not working

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

StreamingResponseBody in Spring Boot uses async request handling, but timeout behavior depends on multiple layers. Developers often set spring.mvc.async.request-timeout and expect immediate effect, then discover the container, proxy, or executor configuration overrides it. Correct timeout handling requires alignment across application, servlet container, and infrastructure.

Core Sections

Start with minimal streaming endpoint

Create a simple stream endpoint first and verify chunked output behavior before tuning timeouts.

java
1@GetMapping(value = "/stream", produces = MediaType.TEXT_PLAIN_VALUE)
2public StreamingResponseBody stream() {
3    return outputStream -> {
4        for (int i = 0; i < 20; i++) {
5            outputStream.write(("chunk-" + i + "\n").getBytes(StandardCharsets.UTF_8));
6            outputStream.flush();
7            Thread.sleep(500);
8        }
9    };
10}

If chunks are not visible incrementally, buffering or proxy behavior may hide timeout symptoms.

Configure Spring MVC async support explicitly

Set default timeout and task executor through WebMvcConfigurer to control async processing behavior.

java
1@Configuration
2public class AsyncConfig implements WebMvcConfigurer {
3
4    @Override
5    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
6        configurer.setDefaultTimeout(30_000L);
7        configurer.setTaskExecutor(streamTaskExecutor());
8    }
9
10    @Bean
11    public AsyncTaskExecutor streamTaskExecutor() {
12        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
13        executor.setCorePoolSize(8);
14        executor.setMaxPoolSize(32);
15        executor.setQueueCapacity(200);
16        executor.setThreadNamePrefix("stream-");
17        executor.initialize();
18        return executor;
19    }
20}

Without a proper executor, blocked threads can cause apparent timeout issues even when timeout properties are correct.

Align servlet container and proxy timeouts

Application timeout alone is not enough. Servlet container connection timeout and reverse proxy read timeout must also be compatible with streaming duration.

yaml
1spring:
2  mvc:
3    async:
4      request-timeout: 30s
5
6server:
7  tomcat:
8    connection-timeout: 30s

If Nginx or another proxy sits in front, increase upstream read timeout accordingly or the client connection may close first.

Understand what timeout means for streaming

For streaming responses, timeout can be tied to async request lifecycle and inactivity. Frequent chunk flushes may keep connection active, so timeout may not trigger as expected during healthy stream output. This is often interpreted as timeout setting not working, when behavior is actually correct for continuous stream.

To test timeout paths, simulate stalled writer behavior instead of active chunk emission.

Handle client disconnects and IOException paths

Streaming code should handle client disconnects gracefully and release resources quickly.

java
1try {
2    outputStream.write(data);
3    outputStream.flush();
4} catch (IOException ex) {
5    log.info("Client disconnected during stream", ex);
6}

Ignoring disconnect exceptions can leave noisy logs and misleading timeout diagnostics.

Add observability for async and stream lifecycle

Log request id, stream start time, chunk count, completion reason, and elapsed time. This makes it clear whether stream ended due to normal completion, timeout, proxy cutoff, or client disconnect.

Metrics on active streams and executor queue depth are especially useful during load spikes.

Test with realistic clients and network paths

Browser behavior, curl behavior, and load balancer behavior can differ. Validate stream and timeout settings through the same path used in production.

A staging test should include long-running stream, intentional stall, and abrupt client disconnect scenarios.

Provide graceful fallback for non-streaming clients

Some clients or intermediaries do not handle streaming responses well. Consider offering a non-streaming endpoint that returns buffered output for compatibility-sensitive integrations. This reduces support burden while keeping streaming benefits for capable clients.

Document timeout behavior per endpoint type so consumers choose the correct interface.

Common Pitfalls

  • Setting only spring.mvc.async.request-timeout and ignoring container or proxy limits.
  • Running streaming on undersized async executor and misreading thread starvation as timeout failure.
  • Expecting timeout during active chunk emission instead of inactivity.
  • Not flushing chunks and then assuming stream timeout logic is broken.
  • Skipping disconnect handling and generating noisy error traces.

Summary

  • StreamingResponseBody timeout behavior is multi-layer, not controller-only.
  • Configure async timeout, executor, servlet container, and proxy settings together.
  • Validate timeout using stalled-stream scenarios, not only active chunk streams.
  • Add lifecycle logging to distinguish timeout from disconnect or infrastructure cutoff.
  • Test through production-like network paths to confirm real behavior.

Course illustration
Course illustration

All Rights Reserved.