From 5eacda7a9d0cbf7384829f5764769a41a6d16bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Plic?= Date: Thu, 29 Sep 2022 21:35:05 +0200 Subject: [PATCH] WebClientEurekaHttpClient - Encode special characters; fixes gh-4121 --- .../http/WebClientEurekaHttpClient.java | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java index 5788c99f52..9684119a01 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2024 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. @@ -17,6 +17,7 @@ package org.springframework.cloud.netflix.eureka.http; import java.util.Map; +import java.util.Optional; import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.InstanceInfo.InstanceStatus; @@ -42,6 +43,7 @@ /** * @author Daniel Lavoie * @author Haytham Mohamed + * @author Václav Plic */ public class WebClientEurekaHttpClient implements EurekaHttpClient { @@ -53,8 +55,8 @@ public WebClientEurekaHttpClient(WebClient webClient) { @Override public EurekaHttpResponse register(InstanceInfo info) { - return webClient.post().uri("apps/" + info.getAppName()).body(BodyInserters.fromValue(info)) - .header(HttpHeaders.ACCEPT_ENCODING, "gzip") + return webClient.post().uri(uriBuilder -> uriBuilder.path("apps/{appName}").build(info.getAppName())) + .body(BodyInserters.fromValue(info)).header(HttpHeaders.ACCEPT_ENCODING, "gzip") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve() .onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse) .block(); @@ -62,19 +64,19 @@ public EurekaHttpResponse register(InstanceInfo info) { @Override public EurekaHttpResponse cancel(String appName, String id) { - return webClient.delete().uri("apps/" + appName + '/' + id).retrieve() - .onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse) - .block(); + return webClient.delete().uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}").build(appName, id)) + .retrieve().onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity() + .map(this::eurekaHttpResponse).block(); } @Override public EurekaHttpResponse sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) { - String urlPath = "apps/" + appName + '/' + id + "?status=" + info.getStatus().toString() - + "&lastDirtyTimestamp=" + info.getLastDirtyTimestamp().toString() - + (overriddenStatus != null ? "&overriddenstatus=" + overriddenStatus.name() : ""); - ResponseEntity response = webClient.put().uri(urlPath) + ResponseEntity response = webClient.put() + .uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}") + .queryParam("status", info.getStatus().toString()) + .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString()).build(appName, id)) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve() .onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(InstanceInfo.class).block(); @@ -95,22 +97,22 @@ public EurekaHttpResponse sendHeartBeat(String appName, String id, @Override public EurekaHttpResponse statusUpdate(String appName, String id, InstanceStatus newStatus, InstanceInfo info) { - String urlPath = "apps/" + appName + '/' + id + "/status?value=" + newStatus.name() + "&lastDirtyTimestamp=" - + info.getLastDirtyTimestamp().toString(); - - return webClient.put().uri(urlPath).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .retrieve().onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity() - .map(this::eurekaHttpResponse).block(); + return webClient.put() + .uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}/status").queryParam("value", newStatus.name()) + .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString()).build(appName, id)) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve() + .onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse) + .block(); } @Override public EurekaHttpResponse deleteStatusOverride(String appName, String id, InstanceInfo info) { - String urlPath = "apps/" + appName + '/' + id + "/status?lastDirtyTimestamp=" - + info.getLastDirtyTimestamp().toString(); - - return webClient.delete().uri(urlPath).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .retrieve().onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity() - .map(this::eurekaHttpResponse).block(); + return webClient.delete() + .uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}/status") + .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString()).build(appName, id)) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve() + .onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse) + .block(); } @Override @@ -119,13 +121,11 @@ public EurekaHttpResponse getApplications(String... regions) { } private EurekaHttpResponse getApplicationsInternal(String urlPath, String[] regions) { - String url = urlPath; - - if (regions != null && regions.length > 0) { - url = url + (urlPath.contains("?") ? "&" : "?") + "regions=" + StringUtil.join(regions); - } + Optional regionsParam = (regions != null && regions.length > 0) ? Optional.of(StringUtil.join(regions)) + : Optional.empty(); - ResponseEntity response = webClient.get().uri(url) + ResponseEntity response = webClient.get() + .uri(uriBuilder -> uriBuilder.path(urlPath).queryParamIfPresent("regions", regionsParam).build()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve() .onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(Applications.class).block(); @@ -156,7 +156,8 @@ public EurekaHttpResponse getSecureVip(String secureVipAddress, St @Override public EurekaHttpResponse getApplication(String appName) { - ResponseEntity response = webClient.get().uri("apps/" + appName) + ResponseEntity response = webClient.get() + .uri(uriBuilder -> uriBuilder.path("apps/{appName}").build(appName)) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve() .onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(Application.class).block(); @@ -170,16 +171,17 @@ public EurekaHttpResponse getApplication(String appName) { @Override public EurekaHttpResponse getInstance(String appName, String id) { - return getInstanceInternal("apps/" + appName + '/' + id); + return getInstanceInternal("apps", appName, id); } @Override public EurekaHttpResponse getInstance(String id) { - return getInstanceInternal("instances/" + id); + return getInstanceInternal("instances", id); } - private EurekaHttpResponse getInstanceInternal(String urlPath) { - ResponseEntity response = webClient.get().uri(urlPath) + private EurekaHttpResponse getInstanceInternal(String... pathSegments) { + ResponseEntity response = webClient.get() + .uri(uriBuilder -> uriBuilder.pathSegment(pathSegments).build()) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve() .onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(InstanceInfo.class).block();