spring-cloud / spring-cloud/spring-cloud-gateway
Spring Cloud Gateway MVC produces OOM when uploading large files
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 4.9k
- Forks
- 3.5k
- Avg merge
- 20h 57m
- Merged PRs (30d)
- 8
Description
Spring Boot 3.2.3, Spring Cloud Gateway MVC 4.4
After migration from ZuulProxy to Spring Cloud Gateway MVC in my application i am encountering an OutOfMemory error when I send a JSON file of approximately 200MB or more to my backend service. This issue does not occur when I switch to Spring Cloud Gateway for testing purposes, but I cannot make this switch in a production environment because I rely on a library provided by a client that does not support WebFlux.
This is my configuration:
spring:
cloud:
gateway:
mvc:
routes:
- id: my-service
uri: lb://my-service
predicates:
- Path=/upload/**
filters:
- StripPrefix=1
- id: my-service-2
uri: lb://my-service
predicates:
- Path=/**
This is the stacktrace:
jakarta.servlet.ServletException: Handler dispatch failed: java.lang.OutOfMemoryError: Java heap space
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1104) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.4.jar:6.1.4]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:547) ~[jakarta.servlet-api-6.0.0.jar:6.0.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.4.jar:6.1.4]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614) ~[jakarta.servlet-api-6.0.0.jar:6.0.0]
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at org.springframework.cloud.gateway.server.mvc.filter.WeightCalculatorFilter.doFilter(WeightCalculatorFilter.java:229) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at org.springframework.cloud.gateway.server.mvc.filter.FormFilter.doFilter(FormFilter.java:93) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:276) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:132) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:256) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:101) ~[undertow-servlet-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:393) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:859) ~[undertow-core-2.3.12.Final.jar:2.3.12.Final]
at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18) ~[jboss-threads-3.5.0.Final.jar:3.5.0.Final]
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513) ~[jboss-threads-3.5.0.Final.jar:3.5.0.Final]
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538) ~[jboss-threads-3.5.0.Final.jar:3.5.0.Final]
at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282) ~[xnio-api-3.8.8.Final.jar:3.8.8.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: java.lang.OutOfMemoryError: Java heap space
at org.springframework.util.FastByteArrayOutputStream.addBuffer(FastByteArrayOutputStream.java:325) ~[spring-core-6.1.4.jar:6.1.4]
at org.springframework.util.FastByteArrayOutputStream.write(FastByteArrayOutputStream.java:126) ~[spring-core-6.1.4.jar:6.1.4]
at java.base/java.io.InputStream.transferTo(InputStream.java:783) ~[na:na]
at org.springframework.util.StreamUtils.copy(StreamUtils.java:150) ~[spring-core-6.1.4.jar:6.1.4]
at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.copyBody(RestClientProxyExchange.java:44) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.lambda$exchange$1(RestClientProxyExchange.java:39) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange$$Lambda$1328/0x000000700174ae38.writeTo(Unknown Source) ~[na:na]
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.lambda$body$2(DefaultRestClient.java:393) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec$$Lambda$1329/0x000000700174b280.writeTo(Unknown Source) ~[na:na]
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:471) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchange(DefaultRestClient.java:449) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.exchange(RestClientProxyExchange.java:40) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at org.springframework.cloud.gateway.server.mvc.handler.ProxyExchangeHandlerFunction.handle(ProxyExchangeHandlerFunction.java:120) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions$LookupProxyExchangeHandlerFunction.handle(HandlerFunctions.java:107) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.function.HandlerFilterFunction$$Lambda$798/0x00000070014b5930.filter(Unknown Source) ~[na:na]
at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.function.HandlerFilterFunction$$Lambda$1263/0x0000007001722300.handle(Unknown Source) ~[na:na]
at org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions.lambda$lb$5(LoadBalancerFilterFunctions.java:112) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions$$Lambda$807/0x00000070014bb1f0.filter(Unknown Source) ~[na:na]
at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.function.HandlerFilterFunction$$Lambda$1263/0x0000007001722300.handle(Unknown Source) ~[na:na]
at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.function.HandlerFilterFunction$$Lambda$798/0x00000070014b5930.filter(Unknown Source) ~[na:na]
at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.function.HandlerFilterFunction$$Lambda$1263/0x0000007001722300.handle(Unknown Source) ~[na:na]
at org.springframework.cloud.gateway.server.mvc.config.RouterFunctionHolderFactory.lambda$getRouterFunction$3(RouterFunctionHolderFactory.java:154) ~[spring-cloud-gateway-server-mvc-4.1.2.jar:4.1.2]
at org.springframework.cloud.gateway.server.mvc.config.RouterFunctionHolderFactory$$Lambda$799/0x00000070014b60a8.filter(Unknown Source) ~[na:na]
at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.function.HandlerFilterFunction$$Lambda$1263/0x0000007001722300.handle(Unknown Source) ~[na:na]
at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.function.HandlerFilterFunction$$Lambda$798/0x00000070014b5930.filter(Unknown Source) ~[na:na]
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with RestClientProxyExchange.copyBody and its use from ProxyExchangeHandlerFunction, then reproduce the approximately 200MB upload through the MVC route shown in the report. Compare the MVC and WebFlux paths while tracing the request body handling; done means the MVC gateway forwards large uploads without exhausting the heap and a regression test covers the behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring-boot
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 28/100