Skip to content

Commit 052b635

Browse files
PhilKesrstoyanchev
authored andcommitted
Apply attributes to the underlying HTTP request
See gh-29958
1 parent 282ee02 commit 052b635

File tree

26 files changed

+374
-24
lines changed

26 files changed

+374
-24
lines changed

spring-test/src/main/java/org/springframework/mock/http/client/reactive/MockClientHttpRequest.java

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ protected void applyCookies() {
119119
.forEach(cookie -> getHeaders().add(HttpHeaders.COOKIE, cookie.toString()));
120120
}
121121

122+
@Override
123+
protected void applyAttributes() {
124+
}
125+
122126
@Override
123127
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
124128
return doCommit(() -> Mono.defer(() -> this.writeHandler.apply(Flux.from(body))));

spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java

+17-5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
9494
@Nullable
9595
private MultiValueMap<String, String> defaultCookies;
9696

97+
private boolean applyAttributes;
98+
9799
@Nullable
98100
private List<ExchangeFilterFunction> filters;
99101

@@ -155,6 +157,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
155157
}
156158
this.defaultCookies = (other.defaultCookies != null ?
157159
new LinkedMultiValueMap<>(other.defaultCookies) : null);
160+
this.applyAttributes = other.applyAttributes;
158161
this.filters = (other.filters != null ? new ArrayList<>(other.filters) : null);
159162
this.entityResultConsumer = other.entityResultConsumer;
160163
this.strategies = other.strategies;
@@ -213,6 +216,12 @@ private MultiValueMap<String, String> initCookies() {
213216
return this.defaultCookies;
214217
}
215218

219+
@Override
220+
public WebTestClient.Builder applyAttributes(boolean applyAttributes) {
221+
this.applyAttributes = applyAttributes;
222+
return this;
223+
}
224+
216225
@Override
217226
public WebTestClient.Builder filter(ExchangeFilterFunction filter) {
218227
Assert.notNull(filter, "ExchangeFilterFunction is required");
@@ -312,22 +321,25 @@ public WebTestClient build() {
312321
this.entityResultConsumer, this.responseTimeout, new DefaultWebTestClientBuilder(this));
313322
}
314323

315-
private static ClientHttpConnector initConnector() {
324+
private ClientHttpConnector initConnector() {
325+
final ClientHttpConnector connector;
316326
if (reactorNettyClientPresent) {
317-
return new ReactorClientHttpConnector();
327+
connector = new ReactorClientHttpConnector();
318328
}
319329
else if (reactorNetty2ClientPresent) {
320330
return new ReactorNetty2ClientHttpConnector();
321331
}
322332
else if (jettyClientPresent) {
323-
return new JettyClientHttpConnector();
333+
connector = new JettyClientHttpConnector();
324334
}
325335
else if (httpComponentsClientPresent) {
326-
return new HttpComponentsClientHttpConnector();
336+
connector = new HttpComponentsClientHttpConnector();
327337
}
328338
else {
329-
return new JdkClientHttpConnector();
339+
connector = new JdkClientHttpConnector();
330340
}
341+
connector.setApplyAttributes(this.applyAttributes);
342+
return connector;
331343
}
332344

333345
private ExchangeStrategies initExchangeStrategies() {

spring-test/src/main/java/org/springframework/test/web/reactive/server/HttpHandlerConnector.java

+12
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public class HttpHandlerConnector implements ClientHttpConnector {
6464

6565
private final HttpHandler handler;
6666

67+
private boolean applyAttributes = true;
68+
6769

6870
/**
6971
* Constructor with the {@link HttpHandler} to handle requests with.
@@ -82,6 +84,16 @@ public Mono<ClientHttpResponse> connect(HttpMethod httpMethod, URI uri,
8284
.subscribeOn(Schedulers.parallel());
8385
}
8486

87+
@Override
88+
public void setApplyAttributes(boolean applyAttributes) {
89+
this.applyAttributes = applyAttributes;
90+
}
91+
92+
@Override
93+
public boolean getApplyAttributes() {
94+
return this.applyAttributes;
95+
}
96+
8597
private Mono<ClientHttpResponse> doConnect(
8698
HttpMethod httpMethod, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
8799

spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java

+7
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,13 @@ interface Builder {
425425
*/
426426
Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
427427

428+
/**
429+
* Global option to specify whether or not attributes should be applied to every request,
430+
* if the used {@link ClientHttpConnector} allows it.
431+
* @param applyAttributes whether or not to apply attributes
432+
*/
433+
Builder applyAttributes(boolean applyAttributes);
434+
428435
/**
429436
* Add the given filter to the filter chain.
430437
* @param filter the filter to be added to the chain

spring-test/src/main/java/org/springframework/test/web/reactive/server/WiretapConnector.java

+12
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ class WiretapConnector implements ClientHttpConnector {
5555

5656
private final Map<String, ClientExchangeInfo> exchanges = new ConcurrentHashMap<>();
5757

58+
private boolean applyAttributes = true;
59+
5860

5961
WiretapConnector(ClientHttpConnector delegate) {
6062
this.delegate = delegate;
@@ -84,6 +86,16 @@ public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri,
8486
});
8587
}
8688

89+
@Override
90+
public void setApplyAttributes(boolean applyAttributes) {
91+
this.applyAttributes = applyAttributes;
92+
}
93+
94+
@Override
95+
public boolean getApplyAttributes() {
96+
return this.applyAttributes;
97+
}
98+
8799
/**
88100
* Create the {@link ExchangeResult} for the given "request-id" header value.
89101
*/

spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java

+12
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ public class MockMvcHttpConnector implements ClientHttpConnector {
8686

8787
private final List<RequestPostProcessor> requestPostProcessors;
8888

89+
private boolean applyAttributes = true;
90+
8991

9092
public MockMvcHttpConnector(MockMvc mockMvc) {
9193
this(mockMvc, Collections.emptyList());
@@ -115,6 +117,16 @@ public Mono<ClientHttpResponse> connect(
115117
}
116118
}
117119

120+
@Override
121+
public void setApplyAttributes(boolean applyAttributes) {
122+
this.applyAttributes = applyAttributes;
123+
}
124+
125+
@Override
126+
public boolean getApplyAttributes() {
127+
return this.applyAttributes;
128+
}
129+
118130
private RequestBuilder adaptRequest(
119131
HttpMethod httpMethod, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
120132

spring-test/src/test/java/org/springframework/test/web/reactive/server/WiretapConnectorTests.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.net.URI;
2020
import java.time.Duration;
21+
import java.util.function.Function;
2122

2223
import org.junit.jupiter.api.Test;
2324
import reactor.core.publisher.Mono;
@@ -48,7 +49,22 @@ public class WiretapConnectorTests {
4849
public void captureAndClaim() {
4950
ClientHttpRequest request = new MockClientHttpRequest(HttpMethod.GET, "/test");
5051
ClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);
51-
ClientHttpConnector connector = (method, uri, fn) -> fn.apply(request).then(Mono.just(response));
52+
ClientHttpConnector connector = new ClientHttpConnector() {
53+
@Override
54+
public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
55+
return requestCallback.apply(request).then(Mono.just(response));
56+
}
57+
58+
@Override
59+
public void setApplyAttributes(boolean applyAttributes) {
60+
61+
}
62+
63+
@Override
64+
public boolean getApplyAttributes() {
65+
return false;
66+
}
67+
};
5268

5369
ClientRequest clientRequest = ClientRequest.create(HttpMethod.GET, URI.create("/test"))
5470
.header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1").build();

spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
package org.springframework.http.client.reactive;
1818

1919
import java.util.ArrayList;
20+
import java.util.Collections;
21+
import java.util.LinkedHashMap;
2022
import java.util.List;
23+
import java.util.Map;
2124
import java.util.concurrent.atomic.AtomicReference;
2225
import java.util.function.Supplier;
2326

@@ -55,6 +58,10 @@ private enum State {NEW, COMMITTING, COMMITTED}
5558

5659
private final MultiValueMap<String, HttpCookie> cookies;
5760

61+
private final Map<String, Object> attributes;
62+
63+
private final boolean applyAttributes;
64+
5865
private final AtomicReference<State> state = new AtomicReference<>(State.NEW);
5966

6067
private final List<Supplier<? extends Publisher<Void>>> commitActions = new ArrayList<>(4);
@@ -64,13 +71,19 @@ private enum State {NEW, COMMITTING, COMMITTED}
6471

6572

6673
public AbstractClientHttpRequest() {
67-
this(new HttpHeaders());
74+
this(new HttpHeaders(), false);
75+
}
76+
77+
public AbstractClientHttpRequest(boolean applyAttributes) {
78+
this(new HttpHeaders(), applyAttributes);
6879
}
6980

70-
public AbstractClientHttpRequest(HttpHeaders headers) {
81+
public AbstractClientHttpRequest(HttpHeaders headers, boolean applyAttributes) {
7182
Assert.notNull(headers, "HttpHeaders must not be null");
7283
this.headers = headers;
7384
this.cookies = new LinkedMultiValueMap<>();
85+
this.attributes = new LinkedHashMap<>();
86+
this.applyAttributes = applyAttributes;
7487
}
7588

7689

@@ -106,6 +119,14 @@ public MultiValueMap<String, HttpCookie> getCookies() {
106119
return this.cookies;
107120
}
108121

122+
@Override
123+
public Map<String, Object> getAttributes() {
124+
if (State.COMMITTED.equals(this.state.get())) {
125+
return Collections.unmodifiableMap(this.attributes);
126+
}
127+
return this.attributes;
128+
}
129+
109130
@Override
110131
public void beforeCommit(Supplier<? extends Mono<Void>> action) {
111132
Assert.notNull(action, "Action must not be null");
@@ -140,6 +161,9 @@ protected Mono<Void> doCommit(@Nullable Supplier<? extends Publisher<Void>> writ
140161
Mono.fromRunnable(() -> {
141162
applyHeaders();
142163
applyCookies();
164+
if (this.applyAttributes) {
165+
applyAttributes();
166+
}
143167
this.state.set(State.COMMITTED);
144168
}));
145169

@@ -168,4 +192,10 @@ protected Mono<Void> doCommit(@Nullable Supplier<? extends Publisher<Void>> writ
168192
*/
169193
protected abstract void applyCookies();
170194

195+
/**
196+
* Add additional attributes from {@link #getAttributes()} to the underlying request.
197+
* This method is called once only.
198+
*/
199+
protected abstract void applyAttributes();
200+
171201
}

spring-web/src/main/java/org/springframework/http/client/reactive/ClientHttpConnector.java

+10
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,14 @@ public interface ClientHttpConnector {
4848
Mono<ClientHttpResponse> connect(HttpMethod method, URI uri,
4949
Function<? super ClientHttpRequest, Mono<Void>> requestCallback);
5050

51+
/**
52+
* Set whether or not attributes should be applied to the underlying http-client library request.
53+
*/
54+
void setApplyAttributes(boolean applyAttributes);
55+
56+
/**
57+
* Whether or not attributes should be applied to the underlying http-client library request.
58+
*/
59+
boolean getApplyAttributes();
60+
5161
}

spring-web/src/main/java/org/springframework/http/client/reactive/ClientHttpRequest.java

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.http.client.reactive;
1818

1919
import java.net.URI;
20+
import java.util.Map;
2021

2122
import org.springframework.http.HttpCookie;
2223
import org.springframework.http.HttpMethod;
@@ -54,4 +55,9 @@ public interface ClientHttpRequest extends ReactiveHttpOutputMessage {
5455
*/
5556
<T> T getNativeRequest();
5657

58+
/**
59+
* Return a mutable map of the request attributes.
60+
*/
61+
Map<String, Object> getAttributes();
62+
5763
}

spring-web/src/main/java/org/springframework/http/client/reactive/ClientHttpRequestDecorator.java

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.http.client.reactive;
1818

1919
import java.net.URI;
20+
import java.util.Map;
2021
import java.util.function.Supplier;
2122

2223
import org.reactivestreams.Publisher;
@@ -85,6 +86,11 @@ public <T> T getNativeRequest() {
8586
return this.delegate.getNativeRequest();
8687
}
8788

89+
@Override
90+
public Map<String, Object> getAttributes() {
91+
return this.delegate.getAttributes();
92+
}
93+
8894
@Override
8995
public void beforeCommit(Supplier<? extends Mono<Void>> action) {
9096
this.delegate.beforeCommit(action);

spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpConnector.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public class HttpComponentsClientHttpConnector implements ClientHttpConnector, C
5959

6060
private DataBufferFactory dataBufferFactory = DefaultDataBufferFactory.sharedInstance;
6161

62+
private boolean applyAttributes = true;
6263

6364
/**
6465
* Default constructor that creates and starts a new instance of {@link CloseableHttpAsyncClient}.
@@ -67,6 +68,7 @@ public HttpComponentsClientHttpConnector() {
6768
this(HttpAsyncClients.createDefault());
6869
}
6970

71+
7072
/**
7173
* Constructor with a pre-configured {@link CloseableHttpAsyncClient} instance.
7274
* @param client the client to use
@@ -111,11 +113,22 @@ public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri,
111113
context.setCookieStore(new BasicCookieStore());
112114
}
113115

114-
HttpComponentsClientHttpRequest request =
115-
new HttpComponentsClientHttpRequest(method, uri, context, this.dataBufferFactory);
116+
HttpComponentsClientHttpRequest request = new HttpComponentsClientHttpRequest(
117+
method, uri, context, this.dataBufferFactory, this.applyAttributes);
118+
116119
return requestCallback.apply(request).then(Mono.defer(() -> execute(request, context)));
117120
}
118121

122+
@Override
123+
public void setApplyAttributes(boolean applyAttributes) {
124+
this.applyAttributes = applyAttributes;
125+
}
126+
127+
@Override
128+
public boolean getApplyAttributes() {
129+
return this.applyAttributes;
130+
}
131+
119132
private Mono<ClientHttpResponse> execute(HttpComponentsClientHttpRequest request, HttpClientContext context) {
120133
AsyncRequestProducer requestProducer = request.toRequestProducer();
121134

spring-web/src/main/java/org/springframework/http/client/reactive/HttpComponentsClientHttpRequest.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
6666

6767

6868
public HttpComponentsClientHttpRequest(HttpMethod method, URI uri, HttpClientContext context,
69-
DataBufferFactory dataBufferFactory) {
70-
69+
DataBufferFactory dataBufferFactory, boolean applyAttributes) {
70+
super(applyAttributes);
7171
this.context = context;
7272
this.httpRequest = new BasicHttpRequest(method.name(), uri);
7373
this.dataBufferFactory = dataBufferFactory;
@@ -157,6 +157,18 @@ protected void applyCookies() {
157157
});
158158
}
159159

160+
/**
161+
* Applies the attributes to the {@link HttpClientContext}.
162+
*/
163+
@Override
164+
protected void applyAttributes() {
165+
getAttributes().forEach((key, value) -> {
166+
if(this.context.getAttribute(key) == null) {
167+
this.context.setAttribute(key, value);
168+
}
169+
});
170+
}
171+
160172
@Override
161173
protected HttpHeaders initReadOnlyHeaders() {
162174
return HttpHeaders.readOnlyHttpHeaders(new HttpComponentsHeadersAdapter(this.httpRequest));

0 commit comments

Comments
 (0)