parent
667004e5fa
commit
1fd6ded7a0
|
@ -37,6 +37,7 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpConnector} for the Java {@link HttpClient}.
|
||||
|
@ -99,7 +100,7 @@ public class JdkClientHttpConnector implements ClientHttpConnector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the underlying {@code HttpClient}'s read timeout as a {@code Duration}.
|
||||
* Set the underlying {@code HttpClient} read timeout as a {@code Duration}.
|
||||
* <p>Default is the system's default timeout.
|
||||
* @since 6.2
|
||||
* @see java.net.http.HttpRequest.Builder#timeout
|
||||
|
@ -126,21 +127,23 @@ public class JdkClientHttpConnector implements ClientHttpConnector {
|
|||
public Mono<ClientHttpResponse> connect(
|
||||
HttpMethod method, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
|
||||
|
||||
JdkClientHttpRequest jdkClientHttpRequest = new JdkClientHttpRequest(method, uri, this.bufferFactory,
|
||||
this.readTimeout);
|
||||
JdkClientHttpRequest request =
|
||||
new JdkClientHttpRequest(method, uri, this.bufferFactory, this.readTimeout);
|
||||
|
||||
return requestCallback.apply(jdkClientHttpRequest).then(Mono.defer(() -> {
|
||||
HttpRequest httpRequest = jdkClientHttpRequest.getNativeRequest();
|
||||
return requestCallback.apply(request).then(Mono.defer(() -> {
|
||||
HttpRequest nativeRequest = request.getNativeRequest();
|
||||
|
||||
CompletableFuture<HttpResponse<Flow.Publisher<List<ByteBuffer>>>> future =
|
||||
this.httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofPublisher());
|
||||
this.httpClient.sendAsync(nativeRequest, HttpResponse.BodyHandlers.ofPublisher());
|
||||
|
||||
return Mono.fromCompletionStage(future)
|
||||
.map(response -> {
|
||||
List<String> headers = response.headers().allValues(HttpHeaders.SET_COOKIE);
|
||||
return new JdkClientHttpResponse(response, this.bufferFactory, this.cookieParser.parse(headers));
|
||||
});
|
||||
return Mono.fromCompletionStage(future).map(response ->
|
||||
new JdkClientHttpResponse(response, this.bufferFactory, parseCookies(response)));
|
||||
}));
|
||||
}
|
||||
|
||||
private MultiValueMap<String, ResponseCookie> parseCookies(HttpResponse<?> response) {
|
||||
List<String> headers = response.headers().allValues(HttpHeaders.SET_COOKIE);
|
||||
return this.cookieParser.parse(headers);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
|
@ -59,8 +59,9 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
|||
private final HttpRequest.Builder builder;
|
||||
|
||||
|
||||
public JdkClientHttpRequest(HttpMethod httpMethod, URI uri, DataBufferFactory bufferFactory,
|
||||
@Nullable Duration readTimeout) {
|
||||
public JdkClientHttpRequest(
|
||||
HttpMethod httpMethod, URI uri, DataBufferFactory bufferFactory, @Nullable Duration readTimeout) {
|
||||
|
||||
Assert.notNull(httpMethod, "HttpMethod is required");
|
||||
Assert.notNull(uri, "URI is required");
|
||||
Assert.notNull(bufferFactory, "DataBufferFactory is required");
|
||||
|
@ -97,32 +98,6 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void applyHeaders() {
|
||||
for (Map.Entry<String, List<String>> entry : getHeaders().headerSet()) {
|
||||
if (entry.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
|
||||
// content-length is specified when writing
|
||||
continue;
|
||||
}
|
||||
for (String value : entry.getValue()) {
|
||||
this.builder.header(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (!getHeaders().containsHeader(HttpHeaders.ACCEPT)) {
|
||||
this.builder.header(HttpHeaders.ACCEPT, "*/*");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyCookies() {
|
||||
MultiValueMap<String, HttpCookie> cookies = getCookies();
|
||||
if (cookies.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.builder.header(HttpHeaders.COOKIE, cookies.values().stream()
|
||||
.flatMap(List::stream).map(HttpCookie::toString).collect(Collectors.joining(";")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
return doCommit(() -> {
|
||||
|
@ -162,4 +137,30 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyHeaders() {
|
||||
for (Map.Entry<String, List<String>> entry : getHeaders().headerSet()) {
|
||||
if (entry.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
|
||||
// content-length is specified when writing
|
||||
continue;
|
||||
}
|
||||
for (String value : entry.getValue()) {
|
||||
this.builder.header(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (!getHeaders().containsHeader(HttpHeaders.ACCEPT)) {
|
||||
this.builder.header(HttpHeaders.ACCEPT, "*/*");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyCookies() {
|
||||
MultiValueMap<String, HttpCookie> cookies = getCookies();
|
||||
if (cookies.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.builder.header(HttpHeaders.COOKIE, cookies.values().stream()
|
||||
.flatMap(List::stream).map(HttpCookie::toString).collect(Collectors.joining(";")));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,7 +47,8 @@ import org.springframework.util.MultiValueMap;
|
|||
*/
|
||||
class JdkClientHttpResponse extends AbstractClientHttpResponse {
|
||||
|
||||
public JdkClientHttpResponse(HttpResponse<Flow.Publisher<List<ByteBuffer>>> response,
|
||||
public JdkClientHttpResponse(
|
||||
HttpResponse<Flow.Publisher<List<ByteBuffer>>> response,
|
||||
DataBufferFactory bufferFactory, MultiValueMap<String, ResponseCookie> cookies) {
|
||||
|
||||
super(HttpStatusCode.valueOf(response.statusCode()),
|
||||
|
@ -63,7 +64,9 @@ class JdkClientHttpResponse extends AbstractClientHttpResponse {
|
|||
return HttpHeaders.readOnlyHttpHeaders(multiValueMap);
|
||||
}
|
||||
|
||||
private static Flux<DataBuffer> adaptBody(HttpResponse<Flow.Publisher<List<ByteBuffer>>> response, DataBufferFactory bufferFactory) {
|
||||
private static Flux<DataBuffer> adaptBody(
|
||||
HttpResponse<Flow.Publisher<List<ByteBuffer>>> response, DataBufferFactory bufferFactory) {
|
||||
|
||||
return JdkFlowAdapter.flowPublisherToFlux(response.body())
|
||||
.flatMapIterable(Function.identity())
|
||||
.map(bufferFactory::wrap)
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.function.Function;
|
|||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.Request;
|
||||
import org.eclipse.jetty.reactive.client.ReactiveResponse;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
@ -32,6 +33,7 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpConnector} for the Jetty Reactive Streams HttpClient.
|
||||
|
@ -126,11 +128,15 @@ public class JettyClientHttpConnector implements ClientHttpConnector {
|
|||
|
||||
private Mono<ClientHttpResponse> execute(JettyClientHttpRequest request) {
|
||||
return Mono.fromDirect(request.toReactiveRequest()
|
||||
.response((reactiveResponse, chunkPublisher) -> {
|
||||
.response((response, chunkPublisher) -> {
|
||||
Flux<DataBuffer> content = Flux.from(chunkPublisher).map(this.bufferFactory::wrap);
|
||||
List<String> headers = reactiveResponse.getHeaders().getValuesList(HttpHeaders.SET_COOKIE);
|
||||
return Mono.just(new JettyClientHttpResponse(reactiveResponse, content, this.cookieParser.parse(headers)));
|
||||
return Mono.just(new JettyClientHttpResponse(response, content, parseCookies(response)));
|
||||
}));
|
||||
}
|
||||
|
||||
private MultiValueMap<String, ResponseCookie> parseCookies(ReactiveResponse response) {
|
||||
List<String> headers = response.getHeaders().getValuesList(HttpHeaders.SET_COOKIE);
|
||||
return this.cookieParser.parse(headers);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
|
@ -72,11 +72,6 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
|||
return this.jettyRequest.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> setComplete() {
|
||||
return doCommit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBufferFactory bufferFactory() {
|
||||
return this.bufferFactory;
|
||||
|
@ -88,6 +83,12 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
|||
return (T) this.jettyRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeaders initReadOnlyHeaders() {
|
||||
return HttpHeaders.readOnlyHttpHeaders(new JettyHeadersAdapter(this.jettyRequest.getHeaders()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
return Mono.<Void>create(sink -> {
|
||||
|
@ -108,13 +109,7 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
|||
.doOnDiscard(DataBuffer.class, DataBufferUtils::release));
|
||||
}
|
||||
|
||||
private String getContentType() {
|
||||
MediaType contentType = getHeaders().getContentType();
|
||||
return contentType != null ? contentType.toString() : MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
}
|
||||
|
||||
private List<Content.Chunk> toContentChunks(DataBuffer dataBuffer) {
|
||||
|
||||
List<Content.Chunk> result = new ArrayList<>(1);
|
||||
DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers();
|
||||
while (iterator.hasNext()) {
|
||||
|
@ -131,11 +126,14 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
|||
return result;
|
||||
}
|
||||
|
||||
private String getContentType() {
|
||||
MediaType contentType = getHeaders().getContentType();
|
||||
return (contentType != null ? contentType.toString() : MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyCookies() {
|
||||
getCookies().values().stream().flatMap(Collection::stream)
|
||||
.map(cookie -> HttpCookie.build(cookie.getName(), cookie.getValue()).build())
|
||||
.forEach(this.jettyRequest::cookie);
|
||||
public Mono<Void> setComplete() {
|
||||
return doCommit();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,8 +148,10 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeaders initReadOnlyHeaders() {
|
||||
return HttpHeaders.readOnlyHttpHeaders(new JettyHeadersAdapter(this.jettyRequest.getHeaders()));
|
||||
protected void applyCookies() {
|
||||
getCookies().values().stream().flatMap(Collection::stream)
|
||||
.map(cookie -> HttpCookie.build(cookie.getName(), cookie.getValue()).build())
|
||||
.forEach(this.jettyRequest::cookie);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
|
@ -37,10 +37,10 @@ import org.springframework.util.MultiValueMap;
|
|||
class JettyClientHttpResponse extends AbstractClientHttpResponse {
|
||||
|
||||
public JettyClientHttpResponse(
|
||||
ReactiveResponse reactiveResponse, Flux<DataBuffer> content,
|
||||
ReactiveResponse response, Flux<DataBuffer> content,
|
||||
MultiValueMap<String, ResponseCookie> cookies) {
|
||||
|
||||
super(HttpStatusCode.valueOf(reactiveResponse.getStatus()), adaptHeaders(reactiveResponse), cookies, content);
|
||||
super(HttpStatusCode.valueOf(response.getStatus()), adaptHeaders(response), cookies, content);
|
||||
}
|
||||
|
||||
private static HttpHeaders adaptHeaders(ReactiveResponse response) {
|
||||
|
|
Loading…
Reference in New Issue