Log unhandled exceptions in WebSocket handlers

Closes gh-1122
This commit is contained in:
rstoyanchev
2025-02-14 11:48:48 +00:00
parent db7804375e
commit 3360e4a46e
3 changed files with 34 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@@ -28,6 +28,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import graphql.ExecutionResult;
import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
@@ -36,6 +38,8 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.graphql.execution.SubscriptionPublisherException;
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.server.WebGraphQlResponse;
import org.springframework.graphql.server.WebSocketGraphQlInterceptor;
@@ -256,7 +260,20 @@ public class GraphQlWebSocketHandler implements WebSocketHandler {
CloseStatus status = new CloseStatus(4409, "Subscriber for " + id + " already exists");
return GraphQlStatus.close(session, status);
}
return Mono.fromCallable(() -> this.codecDelegate.encodeError(session, id, ex));
List<GraphQLError> errors;
if (ex instanceof SubscriptionPublisherException subscriptionEx) {
errors = subscriptionEx.getErrors();
}
else {
if (logger.isErrorEnabled()) {
logger.error("Unresolved " + ex.getClass().getSimpleName() + " for request id " + id, ex);
}
errors = Collections.singletonList(GraphqlErrorBuilder.newError()
.message("Subscription error")
.errorType(ErrorType.INTERNAL_ERROR)
.build());
}
return Mono.fromCallable(() -> this.codecDelegate.encodeError(session, id, errors));
});
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@@ -16,20 +16,16 @@
package org.springframework.graphql.server.webflux;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Decoder;
import org.springframework.core.codec.Encoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.graphql.execution.SubscriptionPublisherException;
import org.springframework.graphql.server.support.GraphQlWebSocketMessage;
import org.springframework.http.MediaType;
import org.springframework.http.codec.CodecConfigurer;
@@ -105,13 +101,7 @@ final class WebSocketCodecDelegate {
return encode(session, GraphQlWebSocketMessage.next(id, responseMap));
}
WebSocketMessage encodeError(WebSocketSession session, String id, Throwable ex) {
List<GraphQLError> errors = ((ex instanceof SubscriptionPublisherException) ?
((SubscriptionPublisherException) ex).getErrors() :
Collections.singletonList(GraphqlErrorBuilder.newError()
.message("Subscription error")
.errorType(ErrorType.INTERNAL_ERROR)
.build()));
WebSocketMessage encodeError(WebSocketSession session, String id, List<GraphQLError> errors) {
return encode(session, GraphQlWebSocketMessage.error(id, errors));
}

View File

@@ -347,12 +347,19 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub
GraphQlStatus.closeSession(session, status);
return Flux.empty();
}
List<GraphQLError> errors = ((ex instanceof SubscriptionPublisherException) ?
((SubscriptionPublisherException) ex).getErrors() :
Collections.singletonList(GraphqlErrorBuilder.newError()
.message("Subscription error")
.errorType(ErrorType.INTERNAL_ERROR)
.build()));
List<GraphQLError> errors;
if (ex instanceof SubscriptionPublisherException subscriptionEx) {
errors = subscriptionEx.getErrors();
}
else {
if (logger.isErrorEnabled()) {
logger.error("Unresolved " + ex.getClass().getSimpleName() + " for request id " + id, ex);
}
errors = Collections.singletonList(GraphqlErrorBuilder.newError()
.message("Subscription error")
.errorType(ErrorType.INTERNAL_ERROR)
.build());
}
return Mono.just(encode(GraphQlWebSocketMessage.error(id, errors)));
});
}