Skip to content

Commit 50faa29

Browse files
committed
Merge branch '5.3.x'
# Conflicts: # build.gradle
2 parents 12315d5 + 67c4b41 commit 50faa29

File tree

6 files changed

+105
-54
lines changed

6 files changed

+105
-54
lines changed

build.gradle

+5-5
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,25 @@ configure(allprojects) { project ->
170170
dependency "org.junit.support:testng-engine:1.0.1"
171171
dependency "org.hamcrest:hamcrest:2.1"
172172
dependency "org.awaitility:awaitility:3.1.6"
173-
dependency "org.assertj:assertj-core:3.21.0"
174-
dependencySet(group: 'org.xmlunit', version: '2.8.3') {
173+
dependency "org.assertj:assertj-core:3.22.0"
174+
dependencySet(group: 'org.xmlunit', version: '2.8.4') {
175175
entry 'xmlunit-assertj'
176176
entry('xmlunit-matchers') {
177177
exclude group: "org.hamcrest", name: "hamcrest-core"
178178
}
179179
}
180-
dependencySet(group: 'org.mockito', version: '4.1.0') {
180+
dependencySet(group: 'org.mockito', version: '4.2.0') {
181181
entry('mockito-core') {
182182
exclude group: "org.hamcrest", name: "hamcrest-core"
183183
}
184184
entry 'mockito-junit-jupiter'
185185
}
186186
dependency "io.mockk:mockk:1.12.1"
187187

188-
dependency("net.sourceforge.htmlunit:htmlunit:2.55.0") {
188+
dependency("net.sourceforge.htmlunit:htmlunit:2.56.0") {
189189
exclude group: "commons-logging", name: "commons-logging"
190190
}
191-
dependency("org.seleniumhq.selenium:htmlunit-driver:2.55.0") {
191+
dependency("org.seleniumhq.selenium:htmlunit-driver:2.56.0") {
192192
exclude group: "commons-logging", name: "commons-logging"
193193
}
194194
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {

spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageReader.java

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -42,6 +42,7 @@
4242
*
4343
* @author Sebastien Deleuze
4444
* @author Rossen Stoyanchev
45+
* @author Juergen Hoeller
4546
* @since 5.0
4647
*/
4748
public class ServerSentEventHttpMessageReader implements HttpMessageReader<Object> {
@@ -140,22 +141,23 @@ public Flux<Object> read(
140141
private Object buildEvent(List<String> lines, ResolvableType valueType, boolean shouldWrap,
141142
Map<String, Object> hints) {
142143

143-
ServerSentEvent.Builder<Object> sseBuilder = shouldWrap ? ServerSentEvent.builder() : null;
144+
ServerSentEvent.Builder<Object> sseBuilder = (shouldWrap ? ServerSentEvent.builder() : null);
144145
StringBuilder data = null;
145146
StringBuilder comment = null;
146147

147148
for (String line : lines) {
148149
if (line.startsWith("data:")) {
149-
data = (data != null ? data : new StringBuilder());
150-
if (line.charAt(5) != ' ') {
151-
data.append(line, 5, line.length());
150+
int length = line.length();
151+
if (length > 5) {
152+
int index = (line.charAt(5) != ' ' ? 5 : 6);
153+
if (length > index) {
154+
data = (data != null ? data : new StringBuilder());
155+
data.append(line, index, line.length());
156+
data.append('\n');
157+
}
152158
}
153-
else {
154-
data.append(line, 6, line.length());
155-
}
156-
data.append('\n');
157159
}
158-
if (shouldWrap) {
160+
else if (shouldWrap) {
159161
if (line.startsWith("id:")) {
160162
sseBuilder.id(line.substring(3).trim());
161163
}

spring-web/src/test/java/org/springframework/http/codec/ServerSentEventHttpMessageReaderTests.java

+15-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -40,6 +40,7 @@
4040
* Unit tests for {@link ServerSentEventHttpMessageReader}.
4141
*
4242
* @author Sebastien Deleuze
43+
* @author Juergen Hoeller
4344
*/
4445
public class ServerSentEventHttpMessageReaderTests extends AbstractLeakCheckingTests {
4546

@@ -66,7 +67,7 @@ public void readServerSentEvents() {
6667
MockServerHttpRequest request = MockServerHttpRequest.post("/")
6768
.body(Mono.just(stringBuffer(
6869
"id:c42\nevent:foo\nretry:123\n:bla\n:bla bla\n:bla bla bla\ndata:bar\n\n" +
69-
"id:c43\nevent:bar\nretry:456\ndata:baz\n\n")));
70+
"id:c43\nevent:bar\nretry:456\ndata:baz\n\ndata:\n\ndata: \n\n")));
7071

7172
Flux<ServerSentEvent> events = this.reader
7273
.read(ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class),
@@ -87,6 +88,8 @@ public void readServerSentEvents() {
8788
assertThat(event.comment()).isNull();
8889
assertThat(event.data()).isEqualTo("baz");
8990
})
91+
.consumeNextWith(event -> assertThat(event.data()).isNull())
92+
.consumeNextWith(event -> assertThat(event.data()).isNull())
9093
.expectComplete()
9194
.verify();
9295
}
@@ -175,7 +178,7 @@ public void readPojo() {
175178
.verify();
176179
}
177180

178-
@Test // gh-24389
181+
@Test // gh-24389
179182
void readPojoWithCommentOnly() {
180183
MockServerHttpRequest request = MockServerHttpRequest.post("/")
181184
.body(Flux.just(stringBuffer(":ping\n"), stringBuffer("\n")));
@@ -221,7 +224,6 @@ public void readError() {
221224

222225
@Test
223226
public void maxInMemoryLimit() {
224-
225227
this.reader.setMaxInMemorySize(17);
226228

227229
MockServerHttpRequest request = MockServerHttpRequest.post("/")
@@ -235,9 +237,8 @@ public void maxInMemoryLimit() {
235237
.verify();
236238
}
237239

238-
@Test // gh-24312
240+
@Test // gh-24312
239241
public void maxInMemoryLimitAllowsReadingPojoLargerThanDefaultSize() {
240-
241242
int limit = this.jsonDecoder.getMaxInMemorySize();
242243

243244
String fooValue = getStringOfSize(limit) + "and then some more";
@@ -259,13 +260,6 @@ public void maxInMemoryLimitAllowsReadingPojoLargerThanDefaultSize() {
259260
.verify();
260261
}
261262

262-
private static String getStringOfSize(long size) {
263-
StringBuilder content = new StringBuilder("Aa");
264-
while (content.length() < size) {
265-
content.append(content);
266-
}
267-
return content.toString();
268-
}
269263

270264
private DataBuffer stringBuffer(String value) {
271265
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
@@ -274,4 +268,12 @@ private DataBuffer stringBuffer(String value) {
274268
return buffer;
275269
}
276270

271+
private static String getStringOfSize(long size) {
272+
StringBuilder content = new StringBuilder("Aa");
273+
while (content.length() < size) {
274+
content.append(content);
275+
}
276+
return content.toString();
277+
}
278+
277279
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -167,7 +167,7 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, Me
167167
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
168168
Object body = NO_VALUE;
169169

170-
EmptyBodyCheckingHttpInputMessage message;
170+
EmptyBodyCheckingHttpInputMessage message = null;
171171
try {
172172
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
173173

@@ -194,6 +194,11 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, Me
194194
catch (IOException ex) {
195195
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
196196
}
197+
finally {
198+
if (message != null && message.hasBody()) {
199+
closeStreamIfNecessary(message.getBody());
200+
}
201+
}
197202

198203
if (body == NO_VALUE) {
199204
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
@@ -296,6 +301,15 @@ protected Object adaptArgumentIfNecessary(@Nullable Object arg, MethodParameter
296301
return arg;
297302
}
298303

304+
/**
305+
* Allow for closing the body stream if necessary,
306+
* e.g. for part streams in a multipart request.
307+
*/
308+
void closeStreamIfNecessary(InputStream body) {
309+
// No-op by default: A standard HttpInputMessage exposes the HTTP request stream
310+
// (ServletRequest#getInputStream), with its lifecycle managed by the container.
311+
}
312+
299313

300314
private static class EmptyBodyCheckingHttpInputMessage implements HttpInputMessage {
301315

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.web.servlet.mvc.method.annotation;
1818

19+
import java.io.IOException;
20+
import java.io.InputStream;
1921
import java.util.List;
2022

2123
import jakarta.servlet.http.HttpServletRequest;
@@ -180,4 +182,17 @@ private String getPartName(MethodParameter methodParam, @Nullable RequestPart re
180182
return partName;
181183
}
182184

185+
@Override
186+
void closeStreamIfNecessary(InputStream body) {
187+
// RequestPartServletServerHttpRequest exposes individual part streams,
188+
// potentially from temporary files -> explicit close call after resolution
189+
// in order to prevent file descriptor leaks.
190+
try {
191+
body.close();
192+
}
193+
catch (IOException ex) {
194+
// ignore
195+
}
196+
}
197+
183198
}

0 commit comments

Comments
 (0)