Skip to content

Commit dd0d270

Browse files
committed
Improve error handling when response is committed
ResponseStatusExceptionHandler lets the error through if it can't change the status while HttpWebHandlerAdapter logs a more helpful message (including status code) but without a full stack trace. Issue: SPR-16231
1 parent 873cb4e commit dd0d270

File tree

3 files changed

+19
-14
lines changed

3 files changed

+19
-14
lines changed

spring-web/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
7171
* <p>TODO:
7272
* This definition is currently duplicated between HttpWebHandlerAdapter
7373
* and AbstractSockJsSession. It is a candidate for a common utility class.
74-
* @see #indicatesDisconnectedClient(Throwable)
74+
* @see #isDisconnectedClientError(Throwable)
7575
*/
7676
private static final Set<String> DISCONNECTED_CLIENT_EXCEPTIONS =
7777
new HashSet<>(Arrays.asList("ClientAbortException", "EOFException", "EofException"));
@@ -158,8 +158,7 @@ public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response)
158158
ServerWebExchange exchange = createExchange(request, response);
159159
return getDelegate().handle(exchange)
160160
.onErrorResume(ex -> {
161-
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
162-
logHandleFailure(ex);
161+
handleFailure(response, ex);
163162
return Mono.empty();
164163
})
165164
.then(Mono.defer(response::setComplete));
@@ -170,8 +169,9 @@ protected ServerWebExchange createExchange(ServerHttpRequest request, ServerHttp
170169
getCodecConfigurer(), getLocaleContextResolver());
171170
}
172171

173-
private void logHandleFailure(Throwable ex) {
174-
if (indicatesDisconnectedClient(ex)) {
172+
private void handleFailure(ServerHttpResponse response, Throwable ex) {
173+
boolean statusCodeChanged = response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
174+
if (isDisconnectedClientError(ex)) {
175175
if (disconnectedClientLogger.isTraceEnabled()) {
176176
disconnectedClientLogger.trace("Looks like the client has gone away", ex);
177177
}
@@ -181,12 +181,16 @@ else if (disconnectedClientLogger.isDebugEnabled()) {
181181
"' to TRACE level.)");
182182
}
183183
}
184+
else if (!statusCodeChanged) {
185+
logger.error("Unhandled failure: " + ex.getMessage() + ", " +
186+
"response already committed with status=" + response.getStatusCode());
187+
}
184188
else {
185189
logger.error("Failed to handle request", ex);
186190
}
187191
}
188192

189-
private boolean indicatesDisconnectedClient(Throwable ex) {
193+
private boolean isDisconnectedClientError(Throwable ex) {
190194
String message = NestedExceptionUtils.getMostSpecificCause(ex).getMessage();
191195
message = (message != null ? message.toLowerCase() : "");
192196
String className = ex.getClass().getSimpleName();

spring-web/src/main/java/org/springframework/web/server/handler/ResponseStatusExceptionHandler.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.apache.commons.logging.LogFactory;
2121
import reactor.core.publisher.Mono;
2222

23+
import org.springframework.http.HttpStatus;
2324
import org.springframework.web.server.ResponseStatusException;
2425
import org.springframework.web.server.ServerWebExchange;
2526
import org.springframework.web.server.WebExceptionHandler;
@@ -39,10 +40,11 @@ public class ResponseStatusExceptionHandler implements WebExceptionHandler {
3940
@Override
4041
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
4142
if (ex instanceof ResponseStatusException) {
42-
// Response may be committed but we'll try..
43-
logger.debug(ex.getMessage());
44-
exchange.getResponse().setStatusCode(((ResponseStatusException) ex).getStatus());
45-
return exchange.getResponse().setComplete();
43+
HttpStatus status = ((ResponseStatusException) ex).getStatus();
44+
if (exchange.getResponse().setStatusCode(status)) {
45+
logger.trace(ex.getMessage());
46+
return exchange.getResponse().setComplete();
47+
}
4648
}
4749
return Mono.error(ex);
4850
}

spring-web/src/test/java/org/springframework/web/server/handler/ResponseStatusExceptionHandlerTests.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,9 @@ public void unresolvedException() throws Exception {
5959
public void responseCommitted() throws Exception {
6060
Throwable ex = new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Oops");
6161
this.exchange.getResponse().setStatusCode(HttpStatus.CREATED);
62-
this.exchange.getResponse().setComplete()
63-
.then(this.handler.handle(this.exchange, ex))
64-
.block(Duration.ofSeconds(5));
65-
assertEquals(HttpStatus.CREATED, this.exchange.getResponse().getStatusCode());
62+
Mono<Void> mono = this.exchange.getResponse().setComplete()
63+
.then(this.handler.handle(this.exchange, ex));
64+
StepVerifier.create(mono).consumeErrorWith(actual -> assertSame(ex, actual)).verify();
6665
}
6766

6867
}

0 commit comments

Comments
 (0)