Remove use of WebClient#exchange from WebTestClient
The exchange() method is now deprecated because it is not safe for general use but that doesn't apply to the WebTestClient because it exposes a different higher level API for response handling that ensures the response is consumed. Nevertheless WebTestClient cannot call WebClient.exchange() any more. To fix this WebTestClient no longer delegates to WebClient and thus gains direct access to the underlying ExchangeFunction. This is not a big deal because WebClient and WebTestClient do the same only when it comes to gathering builder options and request input. See gh-25751
This commit is contained in:
@@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -46,12 +47,17 @@ import org.springframework.test.util.AssertionErrors;
|
||||
import org.springframework.test.util.JsonExpectationsHelper;
|
||||
import org.springframework.test.util.XmlExpectationsHelper;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyInserter;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
import org.springframework.web.util.UriBuilder;
|
||||
import org.springframework.web.util.UriBuilderFactory;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link WebTestClient}.
|
||||
@@ -61,30 +67,42 @@ import org.springframework.web.util.UriBuilder;
|
||||
*/
|
||||
class DefaultWebTestClient implements WebTestClient {
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
private final WiretapConnector wiretapConnector;
|
||||
|
||||
private final Duration timeout;
|
||||
private final ExchangeFunction exchangeFunction;
|
||||
|
||||
private final UriBuilderFactory uriBuilderFactory;
|
||||
|
||||
@Nullable
|
||||
private final HttpHeaders defaultHeaders;
|
||||
|
||||
@Nullable
|
||||
private final MultiValueMap<String, String> defaultCookies;
|
||||
|
||||
private final Duration responseTimeout;
|
||||
|
||||
private final DefaultWebTestClientBuilder builder;
|
||||
|
||||
private final AtomicLong requestIndex = new AtomicLong();
|
||||
|
||||
|
||||
DefaultWebTestClient(WebClient.Builder clientBuilder, ClientHttpConnector connector,
|
||||
@Nullable Duration timeout, DefaultWebTestClientBuilder webTestClientBuilder) {
|
||||
DefaultWebTestClient(ClientHttpConnector connector,
|
||||
Function<ClientHttpConnector, ExchangeFunction> exchangeFactory, UriBuilderFactory uriBuilderFactory,
|
||||
@Nullable HttpHeaders headers, @Nullable MultiValueMap<String, String> cookies,
|
||||
@Nullable Duration responseTimeout, DefaultWebTestClientBuilder clientBuilder) {
|
||||
|
||||
Assert.notNull(clientBuilder, "WebClient.Builder is required");
|
||||
this.wiretapConnector = new WiretapConnector(connector);
|
||||
this.webClient = clientBuilder.clientConnector(this.wiretapConnector).build();
|
||||
this.timeout = (timeout != null ? timeout : Duration.ofSeconds(5));
|
||||
this.builder = webTestClientBuilder;
|
||||
this.exchangeFunction = exchangeFactory.apply(this.wiretapConnector);
|
||||
this.uriBuilderFactory = uriBuilderFactory;
|
||||
this.defaultHeaders = headers;
|
||||
this.defaultCookies = cookies;
|
||||
this.responseTimeout = (responseTimeout != null ? responseTimeout : Duration.ofSeconds(5));
|
||||
this.builder = clientBuilder;
|
||||
}
|
||||
|
||||
|
||||
private Duration getTimeout() {
|
||||
return this.timeout;
|
||||
private Duration getResponseTimeout() {
|
||||
return this.responseTimeout;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,12 +142,12 @@ class DefaultWebTestClient implements WebTestClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodyUriSpec method(HttpMethod method) {
|
||||
return methodInternal(method);
|
||||
public RequestBodyUriSpec method(HttpMethod httpMethod) {
|
||||
return methodInternal(httpMethod);
|
||||
}
|
||||
|
||||
private RequestBodyUriSpec methodInternal(HttpMethod method) {
|
||||
return new DefaultRequestBodyUriSpec(this.webClient.method(method));
|
||||
private RequestBodyUriSpec methodInternal(HttpMethod httpMethod) {
|
||||
return new DefaultRequestBodyUriSpec(httpMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,154 +163,180 @@ class DefaultWebTestClient implements WebTestClient {
|
||||
|
||||
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
|
||||
|
||||
private final WebClient.RequestBodyUriSpec bodySpec;
|
||||
private final HttpMethod httpMethod;
|
||||
|
||||
@Nullable
|
||||
private URI uri;
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
@Nullable
|
||||
private MultiValueMap<String, String> cookies;
|
||||
|
||||
@Nullable
|
||||
private BodyInserter<?, ? super ClientHttpRequest> inserter;
|
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<>(4);
|
||||
|
||||
@Nullable
|
||||
private Consumer<ClientHttpRequest> httpRequestConsumer;
|
||||
|
||||
@Nullable
|
||||
private String uriTemplate;
|
||||
|
||||
private final String requestId;
|
||||
|
||||
DefaultRequestBodyUriSpec(WebClient.RequestBodyUriSpec spec) {
|
||||
this.bodySpec = spec;
|
||||
DefaultRequestBodyUriSpec(HttpMethod httpMethod) {
|
||||
this.httpMethod = httpMethod;
|
||||
this.requestId = String.valueOf(requestIndex.incrementAndGet());
|
||||
this.bodySpec.header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, this.requestId);
|
||||
this.headers = new HttpHeaders();
|
||||
this.headers.add(WebTestClient.WEBTESTCLIENT_REQUEST_ID, this.requestId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
|
||||
this.bodySpec.uri(uriTemplate, uriVariables);
|
||||
this.uriTemplate = uriTemplate;
|
||||
return this;
|
||||
return uri(uriBuilderFactory.expand(uriTemplate, uriVariables));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec uri(String uriTemplate, Map<String, ?> uriVariables) {
|
||||
this.bodySpec.uri(uriTemplate, uriVariables);
|
||||
this.uriTemplate = uriTemplate;
|
||||
return this;
|
||||
return uri(uriBuilderFactory.expand(uriTemplate, uriVariables));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec uri(Function<UriBuilder, URI> uriFunction) {
|
||||
this.bodySpec.uri(uriFunction);
|
||||
this.uriTemplate = null;
|
||||
return this;
|
||||
return uri(uriFunction.apply(uriBuilderFactory.builder()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec uri(URI uri) {
|
||||
this.bodySpec.uri(uri);
|
||||
this.uriTemplate = null;
|
||||
this.uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
private HttpHeaders getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> getCookies() {
|
||||
if (this.cookies == null) {
|
||||
this.cookies = new LinkedMultiValueMap<>(3);
|
||||
}
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec header(String headerName, String... headerValues) {
|
||||
this.bodySpec.header(headerName, headerValues);
|
||||
for (String headerValue : headerValues) {
|
||||
getHeaders().add(headerName, headerValue);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec headers(Consumer<HttpHeaders> headersConsumer) {
|
||||
this.bodySpec.headers(headersConsumer);
|
||||
headersConsumer.accept(getHeaders());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec attribute(String name, Object value) {
|
||||
this.bodySpec.attribute(name, value);
|
||||
this.attributes.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec attributes(
|
||||
Consumer<Map<String, Object>> attributesConsumer) {
|
||||
this.bodySpec.attributes(attributesConsumer);
|
||||
public RequestBodySpec attributes(Consumer<Map<String, Object>> attributesConsumer) {
|
||||
attributesConsumer.accept(this.attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec accept(MediaType... acceptableMediaTypes) {
|
||||
this.bodySpec.accept(acceptableMediaTypes);
|
||||
getHeaders().setAccept(Arrays.asList(acceptableMediaTypes));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec acceptCharset(Charset... acceptableCharsets) {
|
||||
this.bodySpec.acceptCharset(acceptableCharsets);
|
||||
getHeaders().setAcceptCharset(Arrays.asList(acceptableCharsets));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec contentType(MediaType contentType) {
|
||||
this.bodySpec.contentType(contentType);
|
||||
getHeaders().setContentType(contentType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec contentLength(long contentLength) {
|
||||
this.bodySpec.contentLength(contentLength);
|
||||
getHeaders().setContentLength(contentLength);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec cookie(String name, String value) {
|
||||
this.bodySpec.cookie(name, value);
|
||||
getCookies().add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec cookies(
|
||||
Consumer<MultiValueMap<String, String>> cookiesConsumer) {
|
||||
this.bodySpec.cookies(cookiesConsumer);
|
||||
public RequestBodySpec cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer) {
|
||||
cookiesConsumer.accept(getCookies());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec ifModifiedSince(ZonedDateTime ifModifiedSince) {
|
||||
this.bodySpec.ifModifiedSince(ifModifiedSince);
|
||||
getHeaders().setIfModifiedSince(ifModifiedSince);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec ifNoneMatch(String... ifNoneMatches) {
|
||||
this.bodySpec.ifNoneMatch(ifNoneMatches);
|
||||
getHeaders().setIfNoneMatch(Arrays.asList(ifNoneMatches));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestHeadersSpec<?> bodyValue(Object body) {
|
||||
this.bodySpec.bodyValue(body);
|
||||
this.inserter = BodyInserters.fromValue(body);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, S extends Publisher<T>> RequestHeadersSpec<?> body(S publisher, Class<T> elementClass) {
|
||||
this.bodySpec.body(publisher, elementClass);
|
||||
public <T, P extends Publisher<T>> RequestHeadersSpec<?> body(
|
||||
P publisher, ParameterizedTypeReference<T> elementTypeRef) {
|
||||
this.inserter = BodyInserters.fromPublisher(publisher, elementTypeRef);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, S extends Publisher<T>> RequestHeadersSpec<?> body(S publisher, ParameterizedTypeReference<T> elementTypeRef) {
|
||||
this.bodySpec.body(publisher, elementTypeRef);
|
||||
public <T, P extends Publisher<T>> RequestHeadersSpec<?> body(P publisher, Class<T> elementClass) {
|
||||
this.inserter = BodyInserters.fromPublisher(publisher, elementClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestHeadersSpec<?> body(Object producer, Class<?> elementClass) {
|
||||
this.bodySpec.body(producer, elementClass);
|
||||
this.inserter = BodyInserters.fromProducer(producer, elementClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestHeadersSpec<?> body(Object producer, ParameterizedTypeReference<?> elementTypeRef) {
|
||||
this.bodySpec.body(producer, elementTypeRef);
|
||||
this.inserter = BodyInserters.fromProducer(producer, elementTypeRef);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestHeadersSpec<?> body(BodyInserter<?, ? super ClientHttpRequest> inserter) {
|
||||
this.bodySpec.body(inserter);
|
||||
this.inserter = inserter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -304,10 +348,57 @@ class DefaultWebTestClient implements WebTestClient {
|
||||
|
||||
@Override
|
||||
public ResponseSpec exchange() {
|
||||
ClientResponse clientResponse = this.bodySpec.exchange().block(getTimeout());
|
||||
Assert.state(clientResponse != null, "No ClientResponse");
|
||||
ExchangeResult result = wiretapConnector.getExchangeResult(this.requestId, this.uriTemplate, getTimeout());
|
||||
return new DefaultResponseSpec(result, clientResponse, getTimeout());
|
||||
ClientRequest request = (this.inserter != null ?
|
||||
initRequestBuilder().body(this.inserter).build() :
|
||||
initRequestBuilder().build());
|
||||
|
||||
ClientResponse response = exchangeFunction.exchange(request).block(getResponseTimeout());
|
||||
Assert.state(response != null, "No ClientResponse");
|
||||
|
||||
ExchangeResult result = wiretapConnector.getExchangeResult(
|
||||
this.requestId, this.uriTemplate, getResponseTimeout());
|
||||
|
||||
return new DefaultResponseSpec(result, response, getResponseTimeout());
|
||||
}
|
||||
|
||||
private ClientRequest.Builder initRequestBuilder() {
|
||||
ClientRequest.Builder builder = ClientRequest.create(this.httpMethod, initUri())
|
||||
.headers(headers -> headers.addAll(initHeaders()))
|
||||
.cookies(cookies -> cookies.addAll(initCookies()))
|
||||
.attributes(attributes -> attributes.putAll(this.attributes));
|
||||
if (this.httpRequestConsumer != null) {
|
||||
builder.httpRequest(this.httpRequestConsumer);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private URI initUri() {
|
||||
return (this.uri != null ? this.uri : uriBuilderFactory.expand(""));
|
||||
}
|
||||
|
||||
private HttpHeaders initHeaders() {
|
||||
if (CollectionUtils.isEmpty(defaultHeaders)) {
|
||||
return this.headers;
|
||||
}
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
result.putAll(defaultHeaders);
|
||||
result.putAll(this.headers);
|
||||
return result;
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> initCookies() {
|
||||
if (CollectionUtils.isEmpty(this.cookies)) {
|
||||
return (defaultCookies != null ? defaultCookies : new LinkedMultiValueMap<>());
|
||||
}
|
||||
else if (CollectionUtils.isEmpty(defaultCookies)) {
|
||||
return this.cookies;
|
||||
}
|
||||
else {
|
||||
MultiValueMap<String, String> result = new LinkedMultiValueMap<>();
|
||||
result.putAll(defaultCookies);
|
||||
result.putAll(this.cookies);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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,20 +17,30 @@
|
||||
package org.springframework.test.web.reactive.server;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.HttpComponentsClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.JettyClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.http.codec.ClientCodecConfigurer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunctions;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
import org.springframework.web.util.UriBuilderFactory;
|
||||
|
||||
/**
|
||||
@@ -41,7 +51,21 @@ import org.springframework.web.util.UriBuilderFactory;
|
||||
*/
|
||||
class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
||||
|
||||
private final WebClient.Builder webClientBuilder;
|
||||
private static final boolean reactorClientPresent;
|
||||
|
||||
private static final boolean jettyClientPresent;
|
||||
|
||||
private static final boolean httpComponentsClientPresent;
|
||||
|
||||
static {
|
||||
ClassLoader loader = DefaultWebTestClientBuilder.class.getClassLoader();
|
||||
reactorClientPresent = ClassUtils.isPresent("reactor.netty.http.client.HttpClient", loader);
|
||||
jettyClientPresent = ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader);
|
||||
httpComponentsClientPresent =
|
||||
ClassUtils.isPresent("org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient", loader) &&
|
||||
ClassUtils.isPresent("org.apache.hc.core5.reactive.ReactiveDataConsumer", loader);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private final WebHttpHandlerBuilder httpHandlerBuilder;
|
||||
@@ -49,110 +73,178 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
||||
@Nullable
|
||||
private final ClientHttpConnector connector;
|
||||
|
||||
@Nullable
|
||||
private String baseUrl;
|
||||
|
||||
@Nullable
|
||||
private UriBuilderFactory uriBuilderFactory;
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders defaultHeaders;
|
||||
|
||||
@Nullable
|
||||
private MultiValueMap<String, String> defaultCookies;
|
||||
|
||||
@Nullable
|
||||
private List<ExchangeFilterFunction> filters;
|
||||
|
||||
@Nullable
|
||||
private ExchangeStrategies strategies;
|
||||
|
||||
@Nullable
|
||||
private List<Consumer<ExchangeStrategies.Builder>> strategiesConfigurers;
|
||||
|
||||
@Nullable
|
||||
private Duration responseTimeout;
|
||||
|
||||
|
||||
/** Connect to server via Reactor Netty. */
|
||||
/** Determine connector via classpath detection. */
|
||||
DefaultWebTestClientBuilder() {
|
||||
this(new ReactorClientHttpConnector());
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/** Connect to server through the given connector. */
|
||||
DefaultWebTestClientBuilder(ClientHttpConnector connector) {
|
||||
this(null, null, connector, null);
|
||||
}
|
||||
|
||||
/** Connect to given mock server with mock request and response. */
|
||||
/** Use HttpHandlerConnector with mock server. */
|
||||
DefaultWebTestClientBuilder(WebHttpHandlerBuilder httpHandlerBuilder) {
|
||||
this(null, httpHandlerBuilder, null, null);
|
||||
this(httpHandlerBuilder, null);
|
||||
}
|
||||
|
||||
/** Use given connector. */
|
||||
DefaultWebTestClientBuilder(ClientHttpConnector connector) {
|
||||
this(null, connector);
|
||||
}
|
||||
|
||||
DefaultWebTestClientBuilder(
|
||||
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
|
||||
|
||||
Assert.isTrue(httpHandlerBuilder == null || connector == null,
|
||||
"Expected WebHttpHandlerBuilder or ClientHttpConnector but not both.");
|
||||
|
||||
this.connector = connector;
|
||||
this.httpHandlerBuilder = (httpHandlerBuilder != null ? httpHandlerBuilder.clone() : null);
|
||||
}
|
||||
|
||||
/** Copy constructor. */
|
||||
DefaultWebTestClientBuilder(DefaultWebTestClientBuilder other) {
|
||||
this(other.webClientBuilder.clone(), other.httpHandlerBuilder, other.connector,
|
||||
other.responseTimeout);
|
||||
}
|
||||
this.httpHandlerBuilder = (other.httpHandlerBuilder != null ? other.httpHandlerBuilder.clone() : null);
|
||||
this.connector = other.connector;
|
||||
this.responseTimeout = other.responseTimeout;
|
||||
|
||||
private DefaultWebTestClientBuilder(@Nullable WebClient.Builder webClientBuilder,
|
||||
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector,
|
||||
@Nullable Duration responseTimeout) {
|
||||
|
||||
Assert.isTrue(httpHandlerBuilder != null || connector != null,
|
||||
"Either WebHttpHandlerBuilder or ClientHttpConnector must be provided");
|
||||
|
||||
this.webClientBuilder = (webClientBuilder != null ? webClientBuilder : WebClient.builder());
|
||||
this.httpHandlerBuilder = (httpHandlerBuilder != null ? httpHandlerBuilder.clone() : null);
|
||||
this.connector = connector;
|
||||
this.responseTimeout = responseTimeout;
|
||||
this.baseUrl = other.baseUrl;
|
||||
this.uriBuilderFactory = other.uriBuilderFactory;
|
||||
if (other.defaultHeaders != null) {
|
||||
this.defaultHeaders = new HttpHeaders();
|
||||
this.defaultHeaders.putAll(other.defaultHeaders);
|
||||
}
|
||||
else {
|
||||
this.defaultHeaders = null;
|
||||
}
|
||||
this.defaultCookies = (other.defaultCookies != null ?
|
||||
new LinkedMultiValueMap<>(other.defaultCookies) : null);
|
||||
this.filters = (other.filters != null ? new ArrayList<>(other.filters) : null);
|
||||
this.strategies = other.strategies;
|
||||
this.strategiesConfigurers = (other.strategiesConfigurers != null ?
|
||||
new ArrayList<>(other.strategiesConfigurers) : null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder baseUrl(String baseUrl) {
|
||||
this.webClientBuilder.baseUrl(baseUrl);
|
||||
this.baseUrl = baseUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory) {
|
||||
this.webClientBuilder.uriBuilderFactory(uriBuilderFactory);
|
||||
this.uriBuilderFactory = uriBuilderFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder defaultHeader(String headerName, String... headerValues) {
|
||||
this.webClientBuilder.defaultHeader(headerName, headerValues);
|
||||
public WebTestClient.Builder defaultHeader(String header, String... values) {
|
||||
initHeaders().put(header, Arrays.asList(values));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer) {
|
||||
this.webClientBuilder.defaultHeaders(headersConsumer);
|
||||
headersConsumer.accept(initHeaders());
|
||||
return this;
|
||||
}
|
||||
|
||||
private HttpHeaders initHeaders() {
|
||||
if (this.defaultHeaders == null) {
|
||||
this.defaultHeaders = new HttpHeaders();
|
||||
}
|
||||
return this.defaultHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder defaultCookie(String cookie, String... values) {
|
||||
initCookies().addAll(cookie, Arrays.asList(values));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder defaultCookie(String cookieName, String... cookieValues) {
|
||||
this.webClientBuilder.defaultCookie(cookieName, cookieValues);
|
||||
public WebTestClient.Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer) {
|
||||
cookiesConsumer.accept(initCookies());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder defaultCookies(
|
||||
Consumer<MultiValueMap<String, String>> cookiesConsumer) {
|
||||
this.webClientBuilder.defaultCookies(cookiesConsumer);
|
||||
return this;
|
||||
private MultiValueMap<String, String> initCookies() {
|
||||
if (this.defaultCookies == null) {
|
||||
this.defaultCookies = new LinkedMultiValueMap<>(3);
|
||||
}
|
||||
return this.defaultCookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder filter(ExchangeFilterFunction filter) {
|
||||
this.webClientBuilder.filter(filter);
|
||||
Assert.notNull(filter, "ExchangeFilterFunction must not be null");
|
||||
initFilters().add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder filters(Consumer<List<ExchangeFilterFunction>> filtersConsumer) {
|
||||
this.webClientBuilder.filters(filtersConsumer);
|
||||
filtersConsumer.accept(initFilters());
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<ExchangeFilterFunction> initFilters() {
|
||||
if (this.filters == null) {
|
||||
this.filters = new ArrayList<>();
|
||||
}
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder codecs(Consumer<ClientCodecConfigurer> configurer) {
|
||||
this.webClientBuilder.codecs(configurer);
|
||||
if (this.strategiesConfigurers == null) {
|
||||
this.strategiesConfigurers = new ArrayList<>(4);
|
||||
}
|
||||
this.strategiesConfigurers.add(builder -> builder.codecs(configurer));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder exchangeStrategies(ExchangeStrategies strategies) {
|
||||
this.webClientBuilder.exchangeStrategies(strategies);
|
||||
this.strategies = strategies;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public WebTestClient.Builder exchangeStrategies(Consumer<ExchangeStrategies.Builder> configurer) {
|
||||
this.webClientBuilder.exchangeStrategies(configurer);
|
||||
if (this.strategiesConfigurers == null) {
|
||||
this.strategiesConfigurers = new ArrayList<>(4);
|
||||
}
|
||||
this.strategiesConfigurers.add(configurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder apply(WebTestClientConfigurer configurer) {
|
||||
configurer.afterConfigurerAdded(this, this.httpHandlerBuilder, this.connector);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -162,23 +254,62 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient.Builder apply(WebTestClientConfigurer configurer) {
|
||||
configurer.afterConfigurerAdded(this, this.httpHandlerBuilder, this.connector);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WebTestClient build() {
|
||||
ClientHttpConnector connectorToUse = this.connector;
|
||||
if (connectorToUse == null) {
|
||||
Assert.state(this.httpHandlerBuilder != null, "No WebHttpHandlerBuilder available");
|
||||
connectorToUse = new HttpHandlerConnector(this.httpHandlerBuilder.build());
|
||||
if (this.httpHandlerBuilder != null) {
|
||||
connectorToUse = new HttpHandlerConnector(this.httpHandlerBuilder.build());
|
||||
}
|
||||
}
|
||||
if (connectorToUse == null) {
|
||||
connectorToUse = initConnector();
|
||||
}
|
||||
Function<ClientHttpConnector, ExchangeFunction> exchangeFactory = connector -> {
|
||||
ExchangeFunction exchange = ExchangeFunctions.create(connector, initExchangeStrategies());
|
||||
if (CollectionUtils.isEmpty(this.filters)) {
|
||||
return exchange;
|
||||
}
|
||||
return this.filters.stream()
|
||||
.reduce(ExchangeFilterFunction::andThen)
|
||||
.map(filter -> filter.apply(exchange))
|
||||
.orElse(exchange);
|
||||
|
||||
return new DefaultWebTestClient(this.webClientBuilder,
|
||||
connectorToUse, this.responseTimeout, new DefaultWebTestClientBuilder(this));
|
||||
};
|
||||
return new DefaultWebTestClient(connectorToUse, exchangeFactory, initUriBuilderFactory(),
|
||||
this.defaultHeaders != null ? HttpHeaders.readOnlyHttpHeaders(this.defaultHeaders) : null,
|
||||
this.defaultCookies != null ? CollectionUtils.unmodifiableMultiValueMap(this.defaultCookies) : null,
|
||||
this.responseTimeout, new DefaultWebTestClientBuilder(this));
|
||||
}
|
||||
|
||||
private static ClientHttpConnector initConnector() {
|
||||
if (reactorClientPresent) {
|
||||
return new ReactorClientHttpConnector();
|
||||
}
|
||||
else if (jettyClientPresent) {
|
||||
return new JettyClientHttpConnector();
|
||||
}
|
||||
else if (httpComponentsClientPresent) {
|
||||
return new HttpComponentsClientHttpConnector();
|
||||
}
|
||||
throw new IllegalStateException("No suitable default ClientHttpConnector found");
|
||||
}
|
||||
|
||||
private ExchangeStrategies initExchangeStrategies() {
|
||||
if (CollectionUtils.isEmpty(this.strategiesConfigurers)) {
|
||||
return (this.strategies != null ? this.strategies : ExchangeStrategies.withDefaults());
|
||||
}
|
||||
ExchangeStrategies.Builder builder =
|
||||
(this.strategies != null ? this.strategies.mutate() : ExchangeStrategies.builder());
|
||||
this.strategiesConfigurers.forEach(configurer -> configurer.accept(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private UriBuilderFactory initUriBuilderFactory() {
|
||||
if (this.uriBuilderFactory != null) {
|
||||
return this.uriBuilderFactory;
|
||||
}
|
||||
return (this.baseUrl != null ?
|
||||
new DefaultUriBuilderFactory(this.baseUrl) : new DefaultUriBuilderFactory());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user