diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java index ed9e9351c..7fa09749b 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java @@ -16,6 +16,8 @@ package org.springframework.cloud.netflix.eureka.http; +import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.Optional; @@ -44,8 +46,11 @@ import com.netflix.discovery.shared.transport.TransportClientFactory; import org.springframework.cloud.configuration.SSLContextFactory; import org.springframework.cloud.configuration.TlsProperties; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.support.BasicAuthenticationInterceptor; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.client.DefaultResponseErrorHandler; @@ -113,7 +118,9 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor } private RestTemplate restTemplate(String serviceUrl) { - RestTemplate restTemplate = restTemplate(); + ClientHttpRequestFactory requestFactory = this.eurekaClientHttpRequestFactorySupplier + .get(this.sslContext.orElse(null), this.hostnameVerifier.orElse(null)); + RestTemplate restTemplate = new RestTemplate(requestFactory); try { URI serviceURI = new URI(serviceUrl); @@ -132,13 +139,15 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor restTemplate.getMessageConverters().add(0, mappingJacksonHttpMessageConverter()); restTemplate.setErrorHandler(new ErrorHandler()); - return restTemplate; - } + restTemplate.getInterceptors().add((request, body, execution) -> { + ClientHttpResponse response = execution.execute(request, body); + if (!response.getStatusCode().equals(HttpStatus.NOT_FOUND)) { + return response; + } + return new NotFoundHttpResponse(response); + }); - private RestTemplate restTemplate() { - ClientHttpRequestFactory requestFactory = this.eurekaClientHttpRequestFactorySupplier - .get(this.sslContext.orElse(null), this.hostnameVerifier.orElse(null)); - return new RestTemplate(requestFactory); + return restTemplate; } /** @@ -172,11 +181,6 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor @Override public JsonSerializer modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) { - /* - * if (beanDesc.getBeanClass().isAssignableFrom(Applications.class)) { - * return new ApplicationsJsonBeanSerializer((BeanSerializerBase) - * serializer, keyFormatter); } - */ if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) { return new InstanceInfoJsonBeanSerializer((BeanSerializerBase) serializer, false); } @@ -189,6 +193,50 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor public void shutdown() { } + /** + * Response that ignores body, specifically for 404 errors. + */ + private static class NotFoundHttpResponse implements ClientHttpResponse { + + private final ClientHttpResponse response; + + NotFoundHttpResponse(ClientHttpResponse response) { + this.response = response; + } + + @Override + public HttpStatusCode getStatusCode() throws IOException { + return response.getStatusCode(); + } + + @Override + public int getRawStatusCode() throws IOException { + return response.getRawStatusCode(); + } + + @Override + public String getStatusText() throws IOException { + return response.getStatusText(); + } + + @Override + public void close() { + response.close(); + } + + @Override + public InputStream getBody() throws IOException { + // ignore body on 404 for heartbeat, see gh-4145 + return null; + } + + @Override + public HttpHeaders getHeaders() { + return response.getHeaders(); + } + + } + class ErrorHandler extends DefaultResponseErrorHandler { @Override diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java index b19c407a6..5a4a20cd7 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java @@ -38,6 +38,7 @@ import com.netflix.discovery.shared.Applications; import com.netflix.discovery.shared.resolver.EurekaEndpoint; import com.netflix.discovery.shared.transport.EurekaHttpClient; import com.netflix.discovery.shared.transport.TransportClientFactory; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.http.HttpStatus; @@ -151,6 +152,12 @@ public class WebClientTransportClientFactory implements TransportClientFactory { newResponse.body((clientHttpResponse, context) -> clientHttpResponse.getBody()); return Mono.just(newResponse); } + if (clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)) { + ClientResponse newResponse = clientResponse.mutate().statusCode(clientResponse.statusCode()) + // ignore body on 404 for heartbeat, see gh-4145 + .body(Flux.empty()).build(); + return Mono.just(newResponse); + } return Mono.just(clientResponse); }); } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/AbstractEurekaHttpClientTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/AbstractEurekaHttpClientTests.java index 1c842e82e..104331c15 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/AbstractEurekaHttpClientTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/AbstractEurekaHttpClientTests.java @@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.eureka.http; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.shared.Applications; import com.netflix.discovery.shared.transport.EurekaHttpClient; +import com.netflix.discovery.shared.transport.EurekaHttpResponse; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; @@ -48,8 +49,9 @@ abstract class AbstractEurekaHttpClientTests { @Test void testSendHeartBeat() { - assertThat(eurekaHttpClient.sendHeartBeat("test", "test", info, null).getStatusCode()) - .isEqualTo(HttpStatus.OK.value()); + EurekaHttpResponse response = eurekaHttpClient.sendHeartBeat("test", "test", info, null); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.getEntity()).isNotNull(); } @Test @@ -58,6 +60,12 @@ abstract class AbstractEurekaHttpClientTests { .isEqualTo(HttpStatus.NOT_FOUND.value()); } + @Test + void testSendHeartBeatFourOFourWithBody() { + assertThat(eurekaHttpClient.sendHeartBeat("fourOFourWithBody", "test", info, null).getStatusCode()) + .isEqualTo(HttpStatus.NOT_FOUND.value()); + } + @Test void testStatusUpdate() { assertThat(eurekaHttpClient.statusUpdate("test", "test", InstanceInfo.InstanceStatus.UP, info).getStatusCode()) diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java index 073f1ba89..5534f6278 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java @@ -118,6 +118,11 @@ public class EurekaServerMockApplication { if ("fourOFour".equals(appName)) { return new ResponseEntity(HttpStatus.NOT_FOUND); } + if ("fourOFourWithBody".equals(appName)) { + return new ResponseEntity( + "{ \"error\": \"Not Found\", \"message\": null, \"path\": \"/1\", \"requestId\": \"9e5d3244-1\", \"status\": 404, \"timestamp\": \"2023-03-04T03:31:20.810+00:00\" }", + HttpStatus.NOT_FOUND); + } return new ResponseEntity<>(new InstanceInfo(null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, new HashMap<>(), 0L, 0L, null, null), HttpStatus.OK);