Skip to content

Commit cd56b47

Browse files
committed
Merge branch '6.1.x'
2 parents 3547491 + c6b6ccd commit cd56b47

File tree

4 files changed

+53
-39
lines changed

4 files changed

+53
-39
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ public void run() {
337337
logger.trace("Send for " + this.emitter + " failed: " + ex);
338338
}
339339
terminate();
340+
this.emitter.completeWithError(ex);
340341
return;
341342
}
342343
}

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

-20
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,6 @@ public class ResponseBodyEmitter {
7979
@Nullable
8080
private Throwable failure;
8181

82-
/**
83-
* After an I/O error, we don't call {@link #completeWithError} directly but
84-
* wait for the Servlet container to call us via {@code AsyncListener#onError}
85-
* on a container thread at which point we call completeWithError.
86-
* This flag is used to ignore further calls to complete or completeWithError
87-
* that may come for example from an application try-catch block on the
88-
* thread of the I/O error.
89-
*/
90-
private boolean ioErrorOnSend;
91-
9282
private final DefaultCallback timeoutCallback = new DefaultCallback();
9383

9484
private final ErrorCallback errorCallback = new ErrorCallback();
@@ -198,7 +188,6 @@ public synchronized void send(Object object, @Nullable MediaType mediaType) thro
198188
this.handler.send(object, mediaType);
199189
}
200190
catch (IOException ex) {
201-
this.ioErrorOnSend = true;
202191
throw ex;
203192
}
204193
catch (Throwable ex) {
@@ -234,7 +223,6 @@ private void sendInternal(Set<DataWithMediaType> items) throws IOException {
234223
this.handler.send(items);
235224
}
236225
catch (IOException ex) {
237-
this.ioErrorOnSend = true;
238226
throw ex;
239227
}
240228
catch (Throwable ex) {
@@ -255,10 +243,6 @@ private void sendInternal(Set<DataWithMediaType> items) throws IOException {
255243
* related events such as an error while {@link #send(Object) sending}.
256244
*/
257245
public synchronized void complete() {
258-
// Ignore complete after IO failure on send
259-
if (this.ioErrorOnSend) {
260-
return;
261-
}
262246
this.complete = true;
263247
if (this.handler != null) {
264248
this.handler.complete();
@@ -277,10 +261,6 @@ public synchronized void complete() {
277261
* {@link #send(Object) sending}.
278262
*/
279263
public synchronized void completeWithError(Throwable ex) {
280-
// Ignore complete after IO failure on send
281-
if (this.ioErrorOnSend) {
282-
return;
283-
}
284264
this.complete = true;
285265
this.failure = ex;
286266
if (this.handler != null) {

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandlerTests.java

+52-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

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

19+
import java.io.IOException;
1920
import java.util.ArrayList;
2021
import java.util.Arrays;
2122
import java.util.Collections;
@@ -372,6 +373,24 @@ void writeText() throws Exception {
372373
assertThat(emitterHandler.getValuesAsText()).isEqualTo("The quick brown fox jumps over the lazy dog");
373374
}
374375

376+
@Test
377+
void failOnWriteShouldCompleteEmitter() throws Exception {
378+
379+
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
380+
ResponseBodyEmitter emitter = handleValue(sink.asFlux(), Flux.class, forClass(String.class));
381+
382+
ErroringEmitterHandler emitterHandler = new ErroringEmitterHandler();
383+
emitter.initialize(emitterHandler);
384+
385+
sink.tryEmitNext("The quick");
386+
sink.tryEmitNext(" brown fox jumps over ");
387+
sink.tryEmitNext("the lazy dog");
388+
sink.tryEmitComplete();
389+
390+
assertThat(emitterHandler.getHandlingStatus()).isEqualTo(HandlingStatus.ERROR);
391+
assertThat(emitterHandler.getFailure()).isInstanceOf(IOException.class);
392+
}
393+
375394
@Test
376395
void writeFluxOfString() throws Exception {
377396

@@ -451,6 +470,10 @@ private static class EmitterHandler implements ResponseBodyEmitter.Handler {
451470

452471
private final List<Object> values = new ArrayList<>();
453472

473+
private HandlingStatus handlingStatus;
474+
475+
private Throwable failure;
476+
454477

455478
public List<?> getValues() {
456479
return this.values;
@@ -460,22 +483,33 @@ public String getValuesAsText() {
460483
return this.values.stream().map(Object::toString).collect(Collectors.joining());
461484
}
462485

486+
public HandlingStatus getHandlingStatus() {
487+
return this.handlingStatus;
488+
}
489+
490+
public Throwable getFailure() {
491+
return this.failure;
492+
}
493+
463494
@Override
464-
public void send(Object data, MediaType mediaType) {
495+
public void send(Object data, MediaType mediaType) throws IOException {
465496
this.values.add(data);
466497
}
467498

468499
@Override
469-
public void send(Set<ResponseBodyEmitter.DataWithMediaType> items) {
500+
public void send(Set<ResponseBodyEmitter.DataWithMediaType> items) throws IOException {
470501
items.forEach(item -> this.values.add(item.getData()));
471502
}
472503

473504
@Override
474505
public void complete() {
506+
this.handlingStatus = HandlingStatus.SUCCESS;
475507
}
476508

477509
@Override
478510
public void completeWithError(Throwable failure) {
511+
this.handlingStatus = HandlingStatus.ERROR;
512+
this.failure = failure;
479513
}
480514

481515
@Override
@@ -491,6 +525,22 @@ public void onCompletion(Runnable callback) {
491525
}
492526
}
493527

528+
private enum HandlingStatus {
529+
SUCCESS,ERROR
530+
}
531+
532+
private static class ErroringEmitterHandler extends EmitterHandler {
533+
@Override
534+
public void send(Object data, MediaType mediaType) throws IOException {
535+
throw new IOException();
536+
}
537+
538+
@Override
539+
public void send(Set<ResponseBodyEmitter.DataWithMediaType> items) throws IOException {
540+
throw new IOException();
541+
}
542+
}
543+
494544
private static class Bar {
495545

496546
private final String value;

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterTests.java

-17
Original file line numberDiff line numberDiff line change
@@ -148,23 +148,6 @@ void sendWithError() throws Exception {
148148
verifyNoMoreInteractions(this.handler);
149149
}
150150

151-
@Test // gh-30687
152-
void completeIgnoredAfterIOException() throws Exception {
153-
this.emitter.initialize(this.handler);
154-
verify(this.handler).onTimeout(any());
155-
verify(this.handler).onError(any());
156-
verify(this.handler).onCompletion(any());
157-
verifyNoMoreInteractions(this.handler);
158-
159-
willThrow(new IOException()).given(this.handler).send("foo", MediaType.TEXT_PLAIN);
160-
assertThatIOException().isThrownBy(() -> this.emitter.send("foo", MediaType.TEXT_PLAIN));
161-
verify(this.handler).send("foo", MediaType.TEXT_PLAIN);
162-
verifyNoMoreInteractions(this.handler);
163-
164-
this.emitter.complete();
165-
verifyNoMoreInteractions(this.handler);
166-
}
167-
168151
@Test // gh-30687
169152
void completeAfterNonIOException() throws Exception {
170153
this.emitter.initialize(this.handler);

0 commit comments

Comments
 (0)