From fcbd5ec80ab7eef636b48a87f0d2e37edfc150a5 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 20 Oct 2022 14:29:34 +0200 Subject: [PATCH] Avoid NPEs in DefaultServerRequestObservationConvention In some cases, the default response status of a `ServerWebExchange` can be `null`, especially when the response is not available or the server implementation does not set a default response status. This commit ensures that the status code is available when deriving `KeyValue` information from it, or uses a fallback value for the key value. Fixes gh-29359 --- .../DefaultServerRequestObservationConvention.java | 7 ++++--- ...DefaultServerRequestObservationConventionTests.java | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConvention.java b/spring-web/src/main/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConvention.java index b013567d59a..9e3244c7d4c 100644 --- a/spring-web/src/main/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConvention.java +++ b/spring-web/src/main/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConvention.java @@ -100,7 +100,8 @@ public class DefaultServerRequestObservationConvention implements ServerRequestO if (context.isConnectionAborted()) { return STATUS_UNKNOWN; } - return (context.getResponse() != null) ? KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.STATUS, Integer.toString(context.getResponse().getStatusCode().value())) : STATUS_UNKNOWN; + return (context.getResponse() != null && context.getResponse().getStatusCode() != null) ? + KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.STATUS, Integer.toString(context.getResponse().getStatusCode().value())) : STATUS_UNKNOWN; } protected KeyValue uri(ServerRequestObservationContext context) { @@ -112,7 +113,7 @@ public class DefaultServerRequestObservationConvention implements ServerRequestO } return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.URI, pattern.toString()); } - if (context.getResponse() != null) { + if (context.getResponse() != null && context.getResponse().getStatusCode() != null) { HttpStatus status = HttpStatus.resolve(context.getResponse().getStatusCode().value()); if (status != null) { if (status.is3xxRedirection()) { @@ -141,7 +142,7 @@ public class DefaultServerRequestObservationConvention implements ServerRequestO if (context.isConnectionAborted()) { return HTTP_OUTCOME_UNKNOWN; } - if (context.getResponse() != null) { + if (context.getResponse() != null && context.getResponse().getStatusCode() != null) { return HttpOutcome.forStatus(context.getResponse().getStatusCode()); } return HTTP_OUTCOME_UNKNOWN; diff --git a/spring-web/src/test/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConventionTests.java b/spring-web/src/test/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConventionTests.java index e2d5e8538dd..42afdf35c5d 100644 --- a/spring-web/src/test/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConventionTests.java +++ b/spring-web/src/test/java/org/springframework/http/observation/reactive/DefaultServerRequestObservationConventionTests.java @@ -141,6 +141,16 @@ class DefaultServerRequestObservationConventionTests { .contains(KeyValue.of("http.url", "/test/resource")); } + @Test + void supportsNullStatusCode() { + ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/resource")); + ServerRequestObservationContext context = new ServerRequestObservationContext(exchange); + + assertThat(this.convention.getLowCardinalityKeyValues(context)) + .contains(KeyValue.of("status", "UNKNOWN"), + KeyValue.of("exception", "none"), KeyValue.of("outcome", "UNKNOWN")); + } + private static PathPattern getPathPattern(String pattern) { PathPatternParser pathPatternParser = new PathPatternParser(); return pathPatternParser.parse(pattern);