Protect SseEmitter against early I/O errors
Closes gh-25442
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -127,10 +127,14 @@ public class ResponseBodyEmitter {
|
||||
synchronized void initialize(Handler handler) throws IOException {
|
||||
this.handler = handler;
|
||||
|
||||
for (DataWithMediaType sendAttempt : this.earlySendAttempts) {
|
||||
sendInternal(sendAttempt.getData(), sendAttempt.getMediaType());
|
||||
try {
|
||||
for (DataWithMediaType sendAttempt : this.earlySendAttempts) {
|
||||
sendInternal(sendAttempt.getData(), sendAttempt.getMediaType());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.earlySendAttempts.clear();
|
||||
}
|
||||
this.earlySendAttempts.clear();
|
||||
|
||||
if (this.complete) {
|
||||
if (this.failure != null) {
|
||||
@@ -147,6 +151,13 @@ public class ResponseBodyEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void initializeWithError(Throwable ex) {
|
||||
this.complete = true;
|
||||
this.failure = ex;
|
||||
this.earlySendAttempts.clear();
|
||||
this.errorCallback.accept(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after the response is updated with the status code and headers,
|
||||
* if the ResponseBodyEmitter is wrapped in a ResponseEntity, but before the
|
||||
@@ -182,7 +193,9 @@ public class ResponseBodyEmitter {
|
||||
* @throws java.lang.IllegalStateException wraps any other errors
|
||||
*/
|
||||
public synchronized void send(Object object, @Nullable MediaType mediaType) throws IOException {
|
||||
Assert.state(!this.complete, "ResponseBodyEmitter is already set complete");
|
||||
Assert.state(!this.complete,
|
||||
"ResponseBodyEmitter has already completed" +
|
||||
(this.failure != null ? " with error: " + this.failure : ""));
|
||||
sendInternal(object, mediaType);
|
||||
}
|
||||
|
||||
|
||||
@@ -176,10 +176,17 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
|
||||
// Headers will be flushed at the first write
|
||||
outputMessage = new StreamingServletServerHttpResponse(outputMessage);
|
||||
|
||||
DeferredResult<?> deferredResult = new DeferredResult<>(emitter.getTimeout());
|
||||
WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(deferredResult, mavContainer);
|
||||
HttpMessageConvertingHandler handler;
|
||||
try {
|
||||
DeferredResult<?> deferredResult = new DeferredResult<>(emitter.getTimeout());
|
||||
WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(deferredResult, mavContainer);
|
||||
handler = new HttpMessageConvertingHandler(outputMessage, deferredResult);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
emitter.initializeWithError(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
HttpMessageConvertingHandler handler = new HttpMessageConvertingHandler(outputMessage, deferredResult);
|
||||
emitter.initialize(handler);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user