Improve ResponseBodyEmitter error handling

After send error, ignore calls to complete or completeWithError by the
application in the same thread (e.g. try-catch block), to avoid a
competing with an error callback from servlet container during which
the same action is taken.

Issue: SPR-16548
This commit is contained in:
Rossen Stoyanchev 2018-03-07 15:25:31 -05:00
parent e20652009d
commit 568c93457a
1 changed files with 17 additions and 0 deletions

View File

@ -79,6 +79,13 @@ public class ResponseBodyEmitter {
@Nullable
private Throwable failure;
/**
* After an IOException on send, the servlet container will provide an onError
* callback that we'll handle as completeWithError (on container thread).
* We use this flag to ignore competing attempts to completeWithError by
* the application via try-catch. */
private boolean sendFailed;
private final DefaultCallback timeoutCallback = new DefaultCallback();
private final ErrorCallback errorCallback = new ErrorCallback();
@ -182,9 +189,11 @@ public class ResponseBodyEmitter {
this.handler.send(object, mediaType);
}
catch (IOException ex) {
this.sendFailed = true;
throw ex;
}
catch (Throwable ex) {
this.sendFailed = true;
throw new IllegalStateException("Failed to send " + object, ex);
}
}
@ -202,6 +211,10 @@ public class ResponseBodyEmitter {
* related events such as an error while {@link #send(Object) sending}.
*/
public synchronized void complete() {
// Ignore, after send failure
if (this.sendFailed) {
return;
}
this.complete = true;
if (this.handler != null) {
this.handler.complete();
@ -220,6 +233,10 @@ public class ResponseBodyEmitter {
* {@link #send(Object) sending}.
*/
public synchronized void completeWithError(Throwable ex) {
// Ignore, after send failure
if (this.sendFailed) {
return;
}
this.complete = true;
this.failure = ex;
if (this.handler != null) {