Skip to content

Commit 3547491

Browse files
mysend12bclozel
authored andcommitted
Handle DataBufferLimitException as HTTP 413 responses
Prior to this commit, `DataBufferLimitException` would be thrown by codecs when the request body was too large for the configured buffer limit. This exception would not be handled by the web infrastructure and would result in an HTTP 500 server error. This commit introduces a new `PayloadTooLargeException` type that will result in an HTTP 413 "Payload too large" response status. Closes gh-32558
1 parent 80ec951 commit 3547491

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.server;
18+
19+
20+
import org.springframework.http.HttpStatus;
21+
import org.springframework.lang.Nullable;
22+
23+
/**
24+
* Exception for errors that fit response status 413 (payload too large) for use in
25+
* Spring Web applications.
26+
*
27+
* @author Kim Bosung
28+
* @since 6.2
29+
*/
30+
@SuppressWarnings("serial")
31+
public class PayloadTooLargeException extends ResponseStatusException {
32+
33+
public PayloadTooLargeException(@Nullable Throwable cause) {
34+
super(HttpStatus.PAYLOAD_TOO_LARGE, null, cause);
35+
}
36+
37+
}

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
import org.springframework.core.codec.DecodingException;
3535
import org.springframework.core.codec.Hints;
3636
import org.springframework.core.io.buffer.DataBuffer;
37+
import org.springframework.core.io.buffer.DataBufferLimitException;
3738
import org.springframework.core.io.buffer.DataBufferUtils;
3839
import org.springframework.http.HttpHeaders;
3940
import org.springframework.http.HttpMethod;
@@ -51,6 +52,7 @@
5152
import org.springframework.web.bind.support.WebExchangeDataBinder;
5253
import org.springframework.web.reactive.BindingContext;
5354
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
55+
import org.springframework.web.server.PayloadTooLargeException;
5456
import org.springframework.web.server.ResponseStatusException;
5557
import org.springframework.web.server.ServerWebExchange;
5658
import org.springframework.web.server.ServerWebInputException;
@@ -218,7 +220,7 @@ protected Mono<Object> readBody(MethodParameter bodyParam, @Nullable MethodParam
218220
if (contentType == null && SUPPORTED_METHODS.contains(method)) {
219221
Flux<DataBuffer> body = request.getBody().doOnNext(buffer -> {
220222
DataBufferUtils.release(buffer);
221-
// Body not empty, back toy 415..
223+
// Body not empty, back to HTTP 415
222224
throw new UnsupportedMediaTypeStatusException(
223225
mediaType, getSupportedMediaTypes(elementType), elementType);
224226
});
@@ -233,8 +235,13 @@ protected Mono<Object> readBody(MethodParameter bodyParam, @Nullable MethodParam
233235
}
234236

235237
private Throwable handleReadError(MethodParameter parameter, Throwable ex) {
236-
return (ex instanceof DecodingException ?
237-
new ServerWebInputException("Failed to read HTTP message", parameter, ex) : ex);
238+
if (ex instanceof DataBufferLimitException) {
239+
return new PayloadTooLargeException(ex);
240+
}
241+
if (ex instanceof DecodingException) {
242+
return new ServerWebInputException("Failed to read HTTP message", parameter, ex);
243+
}
244+
return ex;
238245
}
239246

240247
private ServerWebInputException handleMissingBody(MethodParameter parameter) {

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
5353
import org.springframework.web.method.HandlerMethod;
5454
import org.springframework.web.reactive.BindingContext;
55+
import org.springframework.web.server.PayloadTooLargeException;
5556
import org.springframework.web.server.ServerWebExchange;
5657
import org.springframework.web.server.ServerWebInputException;
5758
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
@@ -112,6 +113,24 @@ public void emptyBody() {
112113
StepVerifier.create(result).expectError(ServerWebInputException.class).verify();
113114
}
114115

116+
@Test @SuppressWarnings("unchecked")
117+
public void tooLargeBody() {
118+
StringBuilder bodyBuilder = new StringBuilder();
119+
while (bodyBuilder.toString().getBytes().length < 256 * 1024) {
120+
bodyBuilder.append("The default maximum input length is 256kb.");
121+
}
122+
String body = "{\"bar\":\"BARBAR\",\"foo\":\"" + bodyBuilder + "\"}";
123+
124+
MockServerHttpRequest request = post("/path").contentType(MediaType.APPLICATION_JSON).body(body);
125+
ServerWebExchange exchange = MockServerWebExchange.from(request);
126+
ResolvableType type = forClassWithGenerics(Mono.class, TestBean.class);
127+
MethodParameter param = this.testMethod.arg(type);
128+
Mono<TestBean> result = (Mono<TestBean>) this.resolver.readBody(
129+
param, true, this.bindingContext, exchange).block();
130+
131+
StepVerifier.create(result).expectError(PayloadTooLargeException.class).verify();
132+
}
133+
115134
@Test
116135
void monoTestBean() {
117136
String body = "{\"bar\":\"BARBAR\",\"foo\":\"FOOFOO\"}";

0 commit comments

Comments
 (0)