From 0acb1e55137a96e61d13f2df587cd165a58ec53c Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 29 Oct 2020 12:51:47 +0100 Subject: [PATCH] Copy default headers, cookies in WebClient builder This commit makes copies of the default headers and cookies when a WebClient is built, so that subsequent changes to these do not affect previously built clients. Closes: gh-25992 --- .../client/DefaultWebClientBuilder.java | 33 +++++++++++++++++-- .../client/DefaultWebClientTests.java | 32 +++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java index 3b52090258b..b41371de862 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java @@ -263,9 +263,14 @@ final class DefaultWebClientBuilder implements WebClient.Builder { .reduce(ExchangeFilterFunction::andThen) .map(filter -> filter.apply(exchange)) .orElse(exchange) : exchange); + + HttpHeaders defaultHeaders = copyDefaultHeaders(); + + MultiValueMap defaultCookies = copyDefaultCookies(); + return new DefaultWebClient(filteredExchange, initUriBuilderFactory(), - this.defaultHeaders != null ? HttpHeaders.readOnlyHttpHeaders(this.defaultHeaders) : null, - this.defaultCookies != null ? CollectionUtils.unmodifiableMultiValueMap(this.defaultCookies) : null, + defaultHeaders, + defaultCookies, this.defaultRequest, new DefaultWebClientBuilder(this)); } @@ -302,4 +307,28 @@ final class DefaultWebClientBuilder implements WebClient.Builder { return factory; } + @Nullable + private HttpHeaders copyDefaultHeaders() { + if (this.defaultHeaders != null) { + HttpHeaders copy = new HttpHeaders(); + this.defaultHeaders.forEach((key, values) -> copy.put(key, new ArrayList<>(values))); + return HttpHeaders.readOnlyHttpHeaders(copy); + } + else { + return null; + } + } + + @Nullable + private MultiValueMap copyDefaultCookies() { + if (this.defaultCookies != null) { + MultiValueMap copy = new LinkedMultiValueMap<>(this.defaultCookies.size()); + this.defaultCookies.forEach((key, values) -> copy.put(key, new ArrayList<>(values))); + return CollectionUtils.unmodifiableMultiValueMap(copy); + } + else { + return null; + } + } + } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java index 61943fc0b5c..885848770ab 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java @@ -130,7 +130,8 @@ public class DefaultWebClientTests { @Test public void defaultHeaderAndCookie() { WebClient client = this.builder - .defaultHeader("Accept", "application/json").defaultCookie("id", "123") + .defaultHeader("Accept", "application/json") + .defaultCookie("id", "123") .build(); client.get().uri("/path").exchange().block(Duration.ofSeconds(10)); @@ -157,6 +158,35 @@ public class DefaultWebClientTests { assertThat(request.cookies().getFirst("id")).isEqualTo("456"); } + @Test + public void defaultHeaderAndCookieCopies() { + WebClient client1 = this.builder + .defaultHeader("Accept", "application/json") + .defaultCookie("id", "123") + .build(); + WebClient client2 = this.builder + .defaultHeader("Accept", "application/xml") + .defaultCookies(cookies -> cookies.set("id", "456")) + .build(); + + client1.get().uri("/path") + .exchange().block(Duration.ofSeconds(10)); + + ClientRequest request = verifyAndGetRequest(); + assertThat(request.headers().getFirst("Accept")).isEqualTo("application/json"); + assertThat(request.cookies().getFirst("id")).isEqualTo("123"); + + + client2.get().uri("/path") + .exchange().block(Duration.ofSeconds(10)); + + request = verifyAndGetRequest(); + assertThat(request.headers().getFirst("Accept")).isEqualTo("application/xml"); + assertThat(request.cookies().getFirst("id")).isEqualTo("456"); + + + } + @Test public void defaultRequest() { ThreadLocal context = new NamedThreadLocal<>("foo");