From 01935a666b143d671e5d2b9ab793734b3c44de07 Mon Sep 17 00:00:00 2001 From: Carl-Eric Menzel Date: Mon, 11 Sep 2023 15:27:33 +0200 Subject: [PATCH 1/2] Add status handler to recognize unknown status codes outside of 4xx/5xx and throw an UnknownHttpStatusCodeException --- .../springframework/http/HttpStatusCode.java | 9 +++++ .../function/client/DefaultWebClient.java | 9 +++-- .../client/WebClientIntegrationTests.java | 34 +++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java b/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java index 3275eb8d9661..d40be65778db 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java +++ b/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java @@ -88,6 +88,15 @@ default boolean isSameCodeAs(HttpStatusCode other) { return value() == other.value(); } + /** + * Checks whether the given status code is a well-known HTTP status code or not + * @param statusCode the HTTP status code (potentially non-standard) + * @return {@code true} if the status code corresponds to a standard HTTP status code + */ + static boolean isWellKnownStatusCode(HttpStatusCode statusCode) { + return HttpStatus.resolve(statusCode.value()) != null; + } + /** * Return an {@code HttpStatusCode} object for the given integer value. * @param code the status code as integer diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 854cba139e80..762428ab16ab 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -516,9 +516,11 @@ private void initCookies(MultiValueMap out) { private static class DefaultResponseSpec implements ResponseSpec { private static final Predicate STATUS_CODE_ERROR = HttpStatusCode::isError; - - private static final StatusHandler DEFAULT_STATUS_HANDLER = + private static final Predicate STATUS_CODE_UNKNOWN = status -> !HttpStatusCode.isWellKnownStatusCode(status); + private static final StatusHandler DEFAULT_ERROR_STATUS_HANDLER = new StatusHandler(STATUS_CODE_ERROR, ClientResponse::createException); + private static final StatusHandler DEFAULT_UNKNOWN_STATUS_HANDLER = + new StatusHandler(STATUS_CODE_UNKNOWN, ClientResponse::createException); private final HttpMethod httpMethod; @@ -537,7 +539,8 @@ private static class DefaultResponseSpec implements ResponseSpec { this.uri = uri; this.responseMono = responseMono; this.statusHandlers.addAll(defaultStatusHandlers); - this.statusHandlers.add(DEFAULT_STATUS_HANDLER); + this.statusHandlers.add(DEFAULT_ERROR_STATUS_HANDLER); + this.statusHandlers.add(DEFAULT_UNKNOWN_STATUS_HANDLER); this.defaultStatusHandlerCount = this.statusHandlers.size(); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java index 3d7c637f85a7..292c99d3c222 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java @@ -673,6 +673,40 @@ void retrieve555UnknownStatus(ClientHttpConnector connector) { }); } + @ParameterizedWebClientTest + void retrieve929CustomUnknownStatus(ClientHttpConnector connector) { + startServer(connector); + + int errorStatus = 929; + assertThat(HttpStatus.resolve(errorStatus)).isNull(); + String errorMessage = "Something went wrong"; + prepareResponse(response -> response.setResponseCode(errorStatus) + .setHeader("Content-Type", "text/plain").setBody(errorMessage)); + + Mono result = this.webClient.get() + .uri("/unknownPage") + .retrieve() + .bodyToMono(String.class); + + StepVerifier.create(result) + .expectErrorSatisfies(throwable -> { + assertThat(throwable).isInstanceOf(UnknownHttpStatusCodeException.class); + UnknownHttpStatusCodeException ex = (UnknownHttpStatusCodeException) throwable; + assertThat(ex.getMessage()).isEqualTo(("Unknown status code ["+errorStatus+"]")); + assertThat(ex.getStatusCode().value()).isEqualTo(errorStatus); + assertThat(ex.getStatusText()).isEmpty(); + assertThat(ex.getHeaders().getContentType()).isEqualTo(MediaType.TEXT_PLAIN); + assertThat(ex.getResponseBodyAsString()).isEqualTo(errorMessage); + }) + .verify(Duration.ofSeconds(3)); + + expectRequestCount(1); + expectRequest(request -> { + assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo("*/*"); + assertThat(request.getPath()).isEqualTo("/unknownPage"); + }); + } + @ParameterizedWebClientTest void postPojoAsJson(ClientHttpConnector connector) { startServer(connector); From 7428c6dd69b80744f0704c340163c98717fcc43e Mon Sep 17 00:00:00 2001 From: Carl-Eric Menzel Date: Tue, 12 Sep 2023 09:54:23 +0200 Subject: [PATCH 2/2] make isWellKnown an instance method of HttpStatusCode --- .../java/org/springframework/http/HttpStatusCode.java | 9 ++++----- .../web/reactive/function/client/DefaultWebClient.java | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java b/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java index d40be65778db..30acaefb9c11 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java +++ b/spring-web/src/main/java/org/springframework/http/HttpStatusCode.java @@ -89,12 +89,11 @@ default boolean isSameCodeAs(HttpStatusCode other) { } /** - * Checks whether the given status code is a well-known HTTP status code or not - * @param statusCode the HTTP status code (potentially non-standard) - * @return {@code true} if the status code corresponds to a standard HTTP status code + * Checks whether this status code is a well-known HTTP status code or not + * @return {@code true} if the status code corresponds to a standard HTTP status code, {@code false} otherwise */ - static boolean isWellKnownStatusCode(HttpStatusCode statusCode) { - return HttpStatus.resolve(statusCode.value()) != null; + default boolean isWellKnown() { + return HttpStatus.resolve(this.value()) != null; } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 762428ab16ab..460dafb2b086 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -516,7 +516,7 @@ private void initCookies(MultiValueMap out) { private static class DefaultResponseSpec implements ResponseSpec { private static final Predicate STATUS_CODE_ERROR = HttpStatusCode::isError; - private static final Predicate STATUS_CODE_UNKNOWN = status -> !HttpStatusCode.isWellKnownStatusCode(status); + private static final Predicate STATUS_CODE_UNKNOWN = status -> !status.isWellKnown(); private static final StatusHandler DEFAULT_ERROR_STATUS_HANDLER = new StatusHandler(STATUS_CODE_ERROR, ClientResponse::createException); private static final StatusHandler DEFAULT_UNKNOWN_STATUS_HANDLER =