Skip to content

Commit dcc7154

Browse files
committed
Polishing contribution
See gh-23432
1 parent d930617 commit dcc7154

File tree

6 files changed

+158
-126
lines changed

6 files changed

+158
-126
lines changed

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,37 +27,40 @@
2727
import org.springframework.http.HttpMethod;
2828

2929
/**
30-
* {@link ClientHttpConnector} for the Java 11 HTTP client.
30+
* {@link ClientHttpConnector} for Java's {@link HttpClient}.
3131
*
3232
* @author Julien Eyraud
33-
* @since 5.2
34-
* @see <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html">Java HttpClient</a>
33+
* @since 6.0
34+
* @see <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html">HttpClient</a>
3535
*/
3636
public class JdkClientHttpConnector implements ClientHttpConnector {
3737

3838
private final HttpClient httpClient;
3939

40-
private final DataBufferFactory dataBufferFactory;
40+
private final DataBufferFactory bufferFactory;
4141

4242

4343
/**
44-
* Default constructor that creates a new instance of {@link HttpClient} and a {@link DataBufferFactory}.
44+
* Default constructor that uses {@link HttpClient#newHttpClient()}.
4545
*/
4646
public JdkClientHttpConnector() {
4747
this(HttpClient.newHttpClient(), new DefaultDataBufferFactory());
4848
}
4949

5050
/**
51-
* Constructor with an initialized {@link HttpClient} and a initialized {@link DataBufferFactory}.
51+
* Constructor with an initialized {@link HttpClient} and a {@link DataBufferFactory}.
5252
*/
53-
public JdkClientHttpConnector(final HttpClient httpClient, final DataBufferFactory dataBufferFactory) {
53+
public JdkClientHttpConnector(HttpClient httpClient, DataBufferFactory bufferFactory) {
5454
this.httpClient = httpClient;
55-
this.dataBufferFactory = dataBufferFactory;
55+
this.bufferFactory = bufferFactory;
5656
}
5757

58+
5859
@Override
59-
public Mono<ClientHttpResponse> connect(final HttpMethod method, final URI uri, final Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
60-
final JdkClientHttpRequest request = new JdkClientHttpRequest(this.httpClient, method, uri, this.dataBufferFactory);
60+
public Mono<ClientHttpResponse> connect(
61+
HttpMethod method, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
62+
63+
JdkClientHttpRequest request = new JdkClientHttpRequest(this.httpClient, method, uri, this.bufferFactory);
6164
return requestCallback.apply(request).then(Mono.defer(request::getResponse));
6265
}
6366

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

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,18 +37,20 @@
3737
import org.springframework.core.io.buffer.DataBufferFactory;
3838
import org.springframework.http.HttpHeaders;
3939
import org.springframework.http.HttpMethod;
40+
import org.springframework.lang.Nullable;
4041
import org.springframework.util.Assert;
4142

4243
/**
43-
* {@link ClientHttpRequest} implementation for the Java 11 HTTP client.
44+
* {@link ClientHttpRequest} implementation for Java's {@link HttpClient}.
4445
*
4546
* @author Julien Eyraud
46-
* @since 5.2
47-
* @see <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html">Java HttpClient</a>
47+
* @since 6.0
4848
*/
4949
class JdkClientHttpRequest extends AbstractClientHttpRequest {
5050

51-
private static final Set<String> DISALLOWED_HEADERS = Set.of("connection", "content-length", "date", "expect", "from", "host", "upgrade", "via", "warning");
51+
private static final Set<String> DISALLOWED_HEADERS =
52+
Set.of("connection", "content-length", "date", "expect", "from", "host", "upgrade", "via", "warning");
53+
5254

5355
private final HttpClient httpClient;
5456

@@ -60,41 +62,25 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
6062

6163
private final DataBufferFactory bufferFactory;
6264

65+
@Nullable
6366
private Mono<ClientHttpResponse> response;
6467

6568

66-
public JdkClientHttpRequest(final HttpClient httpClient, final HttpMethod httpMethod, final URI uri, final DataBufferFactory bufferFactory) {
69+
public JdkClientHttpRequest(
70+
HttpClient httpClient, HttpMethod httpMethod, URI uri, DataBufferFactory bufferFactory) {
71+
6772
Assert.notNull(httpClient, "HttpClient should not be null");
6873
Assert.notNull(httpMethod, "HttpMethod should not be null");
6974
Assert.notNull(uri, "URI should not be null");
7075
Assert.notNull(bufferFactory, "DataBufferFactory should not be null");
76+
7177
this.httpClient = httpClient;
7278
this.method = httpMethod;
7379
this.uri = uri;
7480
this.builder = HttpRequest.newBuilder(uri);
7581
this.bufferFactory = bufferFactory;
7682
}
7783

78-
@Override
79-
protected void applyHeaders() {
80-
HttpHeaders headers = getHeaders();
81-
for (Map.Entry<String, List<String>> header : getHeaders().entrySet()) {
82-
if (!DISALLOWED_HEADERS.contains(header.getKey().toLowerCase())) {
83-
for (String value : header.getValue()) {
84-
this.builder.header(header.getKey(), value);
85-
}
86-
}
87-
}
88-
if (!headers.containsKey(HttpHeaders.ACCEPT)) {
89-
this.builder.header(HttpHeaders.ACCEPT, "*/*");
90-
}
91-
}
92-
93-
@Override
94-
protected void applyCookies() {
95-
final String cookies = getCookies().values().stream().flatMap(List::stream).map(c -> c.getName() + "=" + c.getValue()).collect(Collectors.joining("; "));
96-
this.builder.header(HttpHeaders.COOKIE, cookies);
97-
}
9884

9985
@Override
10086
public HttpMethod getMethod() {
@@ -117,16 +103,52 @@ public <T> T getNativeRequest() {
117103
return (T) this.builder.build();
118104
}
119105

106+
Mono<ClientHttpResponse> getResponse() {
107+
Assert.notNull(this.response, "Response is not set");
108+
return this.response;
109+
}
110+
111+
112+
@Override
113+
protected void applyHeaders() {
114+
for (Map.Entry<String, List<String>> header : getHeaders().entrySet()) {
115+
if (DISALLOWED_HEADERS.contains(header.getKey().toLowerCase())) {
116+
continue;
117+
}
118+
for (String value : header.getValue()) {
119+
this.builder.header(header.getKey(), value);
120+
}
121+
}
122+
if (!getHeaders().containsKey(HttpHeaders.ACCEPT)) {
123+
this.builder.header(HttpHeaders.ACCEPT, "*/*");
124+
}
125+
}
126+
127+
@Override
128+
protected void applyCookies() {
129+
this.builder.header(HttpHeaders.COOKIE,
130+
getCookies().values().stream()
131+
.flatMap(List::stream)
132+
.map(cookie -> cookie.getName() + "=" + cookie.getValue())
133+
.collect(Collectors.joining("; ")));
134+
}
135+
120136
@Override
121-
public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
137+
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
122138
return doCommit(() -> {
123-
final Flow.Publisher<ByteBuffer> flowAdapter = JdkFlowAdapter.publisherToFlowPublisher(Flux.from(body).map(DataBuffer::asByteBuffer));
124-
final long contentLength = getHeaders().getContentLength();
125-
final HttpRequest.BodyPublisher bodyPublisher = contentLength >= 0 ? HttpRequest.BodyPublishers.fromPublisher(flowAdapter, contentLength)
126-
: HttpRequest.BodyPublishers.fromPublisher(flowAdapter);
127-
this.response = Mono
128-
.fromCompletionStage(() -> this.httpClient.sendAsync(this.builder.method(this.method.name(), bodyPublisher).build(), HttpResponse.BodyHandlers.ofPublisher()))
129-
.map(r -> new JdkClientHttpResponse(r, this.bufferFactory));
139+
Flow.Publisher<ByteBuffer> flow =
140+
JdkFlowAdapter.publisherToFlowPublisher(Flux.from(body).map(DataBuffer::asByteBuffer));
141+
142+
HttpRequest.BodyPublisher bodyPublisher = (getHeaders().getContentLength() >= 0 ?
143+
HttpRequest.BodyPublishers.fromPublisher(flow, getHeaders().getContentLength()) :
144+
HttpRequest.BodyPublishers.fromPublisher(flow));
145+
146+
this.response = Mono.fromCompletionStage(() -> {
147+
HttpRequest request = this.builder.method(this.method.name(), bodyPublisher).build();
148+
return this.httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofPublisher());
149+
})
150+
.map(response -> new JdkClientHttpResponse(response, this.bufferFactory));
151+
130152
return Mono.empty();
131153
});
132154
}
@@ -141,17 +163,17 @@ public Mono<Void> setComplete() {
141163
if (isCommitted()) {
142164
return Mono.empty();
143165
}
144-
else {
145-
return doCommit(() -> {
146-
this.response = Mono
147-
.fromCompletionStage(() -> this.httpClient.sendAsync(this.builder.method(this.method.name(), HttpRequest.BodyPublishers.noBody()).build(), HttpResponse.BodyHandlers.ofPublisher()))
148-
.map(r -> new JdkClientHttpResponse(r, this.bufferFactory));
149-
return Mono.empty();
150-
});
151-
}
152-
}
153166

154-
public Mono<ClientHttpResponse> getResponse() {
155-
return this.response;
167+
return doCommit(() -> {
168+
this.response = Mono.fromCompletionStage(() -> {
169+
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.noBody();
170+
HttpRequest request = this.builder.method(this.method.name(), bodyPublisher).build();
171+
return this.httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofPublisher());
172+
})
173+
.map(response -> new JdkClientHttpResponse(response, this.bufferFactory));
174+
175+
return Mono.empty();
176+
});
156177
}
178+
157179
}
Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,10 +17,12 @@
1717
package org.springframework.http.client.reactive;
1818

1919
import java.net.HttpCookie;
20+
import java.net.http.HttpClient;
2021
import java.net.http.HttpResponse;
2122
import java.nio.ByteBuffer;
2223
import java.util.List;
2324
import java.util.concurrent.Flow;
25+
import java.util.function.Function;
2426
import java.util.regex.Matcher;
2527
import java.util.regex.Pattern;
2628

@@ -38,70 +40,77 @@
3840
import org.springframework.util.MultiValueMap;
3941

4042
/**
41-
* {@link ClientHttpResponse} implementation for the Java 11 HTTP client.
43+
* {@link ClientHttpResponse} implementation for Java's {@link HttpClient}.
4244
*
4345
* @author Julien Eyraud
44-
* @since 5.2
45-
* @see <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html">Java HttpClient</a>
46+
* @since 6.0
4647
*/
4748
class JdkClientHttpResponse implements ClientHttpResponse {
48-
private static final Pattern SAMESITE_PATTERN = Pattern.compile("(?i).*SameSite=(Strict|Lax|None).*");
49+
50+
private static final Pattern SAME_SITE_PATTERN = Pattern.compile("(?i).*SameSite=(Strict|Lax|None).*");
51+
4952

5053
private final HttpResponse<Flow.Publisher<List<ByteBuffer>>> response;
5154

5255
private final DataBufferFactory bufferFactory;
5356

5457

55-
public JdkClientHttpResponse(final HttpResponse<Flow.Publisher<List<ByteBuffer>>> response, final DataBufferFactory bufferFactory) {
58+
public JdkClientHttpResponse(
59+
HttpResponse<Flow.Publisher<List<ByteBuffer>>> response, DataBufferFactory bufferFactory) {
60+
5661
this.response = response;
5762
this.bufferFactory = bufferFactory;
5863
}
5964

60-
@Nullable
61-
private static String parseSameSite(String headerValue) {
62-
Matcher matcher = SAMESITE_PATTERN.matcher(headerValue);
63-
return (matcher.matches() ? matcher.group(1) : null);
64-
}
6565

6666
@Override
6767
public HttpStatus getStatusCode() {
68-
return HttpStatus.resolve(this.response.statusCode());
68+
return HttpStatus.valueOf(this.response.statusCode());
6969
}
7070

7171
@Override
7272
public int getRawStatusCode() {
7373
return this.response.statusCode();
7474
}
7575

76+
@Override
77+
public HttpHeaders getHeaders() {
78+
return this.response.headers().map().entrySet().stream()
79+
.collect(HttpHeaders::new,
80+
(headers, entry) -> headers.addAll(entry.getKey(), entry.getValue()),
81+
HttpHeaders::addAll);
82+
}
83+
7684
@Override
7785
public MultiValueMap<String, ResponseCookie> getCookies() {
7886
return this.response.headers().allValues(HttpHeaders.SET_COOKIE).stream()
7987
.flatMap(header ->
80-
HttpCookie.parse(header).stream().map(httpCookie ->
81-
ResponseCookie
82-
.from(httpCookie.getName(), httpCookie.getValue())
83-
.domain(httpCookie.getDomain())
84-
.httpOnly(httpCookie.isHttpOnly())
85-
.maxAge(httpCookie.getMaxAge())
86-
.path(httpCookie.getPath())
87-
.secure(httpCookie.getSecure())
88+
HttpCookie.parse(header).stream().map(cookie ->
89+
ResponseCookie.from(cookie.getName(), cookie.getValue())
90+
.domain(cookie.getDomain())
91+
.httpOnly(cookie.isHttpOnly())
92+
.maxAge(cookie.getMaxAge())
93+
.path(cookie.getPath())
94+
.secure(cookie.getSecure())
8895
.sameSite(parseSameSite(header))
89-
.build()
90-
)
91-
).collect(LinkedMultiValueMap::new, (m, v) -> m.add(v.getName(), v), LinkedMultiValueMap::addAll);
96+
.build()))
97+
.collect(LinkedMultiValueMap::new,
98+
(valueMap, cookie) -> valueMap.add(cookie.getName(), cookie),
99+
LinkedMultiValueMap::addAll);
100+
}
101+
102+
@Nullable
103+
private static String parseSameSite(String headerValue) {
104+
Matcher matcher = SAME_SITE_PATTERN.matcher(headerValue);
105+
return (matcher.matches() ? matcher.group(1) : null);
92106
}
93107

94108
@Override
95109
public Flux<DataBuffer> getBody() {
96-
return JdkFlowAdapter
97-
.flowPublisherToFlux(this.response.body())
98-
.flatMap(Flux::fromIterable)
110+
return JdkFlowAdapter.flowPublisherToFlux(this.response.body())
111+
.flatMapIterable(Function.identity())
99112
.map(this.bufferFactory::wrap)
100113
.doOnDiscard(DataBuffer.class, DataBufferUtils::release);
101114
}
102115

103-
@Override
104-
public HttpHeaders getHeaders() {
105-
return this.response.headers().map().entrySet().stream().collect(HttpHeaders::new, (headers, entry) -> headers.addAll(entry.getKey(), entry.getValue()), HttpHeaders::addAll);
106-
}
107116
}

spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)