Skip to content

Commit 21c3d4f

Browse files
committed
Support Jetty 10 in JettyClientHttpRequest
Though Jetty 10 was previously supported in the JettyClientHttpResponse, this commit ensures support in the JettyClientHttpRequest. Closes gh-29867 See gh-26123
1 parent de53d77 commit 21c3d4f

File tree

3 files changed

+103
-61
lines changed

3 files changed

+103
-61
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.http.client.reactive;
18+
19+
import java.lang.reflect.Method;
20+
21+
import org.eclipse.jetty.client.api.Request;
22+
import org.eclipse.jetty.client.api.Response;
23+
24+
import org.springframework.http.HttpHeaders;
25+
import org.springframework.lang.Nullable;
26+
import org.springframework.util.Assert;
27+
import org.springframework.util.ReflectionUtils;
28+
29+
/**
30+
* Used to support Jetty 10.
31+
*
32+
* @author Rossen Stoyanchev
33+
* @author Arjen Poutsma
34+
* @since 5.3.26
35+
*/
36+
abstract class Jetty10HttpFieldsHelper {
37+
38+
private static final boolean jetty10Present;
39+
40+
private static final Method requestGetHeadersMethod;
41+
42+
private static final Method responseGetHeadersMethod;
43+
44+
private static final Method getNameMethod;
45+
46+
private static final Method getValueMethod;
47+
48+
49+
static {
50+
try {
51+
ClassLoader classLoader = JettyClientHttpResponse.class.getClassLoader();
52+
Class<?> httpFieldsClass = classLoader.loadClass("org.eclipse.jetty.http.HttpFields");
53+
jetty10Present = httpFieldsClass.isInterface();
54+
requestGetHeadersMethod = Request.class.getMethod("getHeaders");
55+
responseGetHeadersMethod = Response.class.getMethod("getHeaders");
56+
Class<?> httpFieldClass = classLoader.loadClass("org.eclipse.jetty.http.HttpField");
57+
getNameMethod = httpFieldClass.getMethod("getName");
58+
getValueMethod = httpFieldClass.getMethod("getValue");
59+
}
60+
catch (ClassNotFoundException | NoSuchMethodException ex) {
61+
throw new IllegalStateException("No compatible Jetty version found", ex);
62+
}
63+
}
64+
65+
66+
public static boolean jetty10Present() {
67+
return jetty10Present;
68+
}
69+
70+
public static HttpHeaders getHttpHeaders(Request request) {
71+
Iterable<?> iterator = (Iterable<?>)
72+
ReflectionUtils.invokeMethod(requestGetHeadersMethod, request);
73+
return getHttpHeadersInternal(iterator);
74+
}
75+
76+
public static HttpHeaders getHttpHeaders(Response response) {
77+
Iterable<?> iterator = (Iterable<?>)
78+
ReflectionUtils.invokeMethod(responseGetHeadersMethod, response);
79+
return getHttpHeadersInternal(iterator);
80+
}
81+
82+
private static HttpHeaders getHttpHeadersInternal(@Nullable Iterable<?> iterator) {
83+
Assert.notNull(iterator, "Iterator must not be null");
84+
HttpHeaders headers = new HttpHeaders();
85+
for (Object field : iterator) {
86+
String name = (String) ReflectionUtils.invokeMethod(getNameMethod, field);
87+
Assert.notNull(name, "Header name must not be null");
88+
String value = (String) ReflectionUtils.invokeMethod(getValueMethod, field);
89+
headers.add(name, value);
90+
}
91+
return headers;
92+
}
93+
}

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 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,6 +37,7 @@
3737
import org.springframework.http.HttpHeaders;
3838
import org.springframework.http.HttpMethod;
3939
import org.springframework.http.MediaType;
40+
import org.springframework.util.MultiValueMap;
4041

4142
/**
4243
* {@link ClientHttpRequest} implementation for the Jetty ReactiveStreams HTTP client.
@@ -54,7 +55,6 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
5455
private final ReactiveRequest.Builder builder;
5556

5657

57-
5858
public JettyClientHttpRequest(Request jettyRequest, DataBufferFactory bufferFactory) {
5959
this.jettyRequest = jettyRequest;
6060
this.bufferFactory = bufferFactory;
@@ -144,7 +144,11 @@ protected void applyHeaders() {
144144

145145
@Override
146146
protected HttpHeaders initReadOnlyHeaders() {
147-
return HttpHeaders.readOnlyHttpHeaders(new JettyHeadersAdapter(this.jettyRequest.getHeaders()));
147+
MultiValueMap<String, String> headers = (Jetty10HttpFieldsHelper.jetty10Present() ?
148+
Jetty10HttpFieldsHelper.getHttpHeaders(this.jettyRequest) :
149+
new JettyHeadersAdapter(this.jettyRequest.getHeaders()));
150+
151+
return HttpHeaders.readOnlyHttpHeaders(headers);
148152
}
149153

150154
public ReactiveRequest toReactiveRequest() {

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

+3-58
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -16,13 +16,11 @@
1616

1717
package org.springframework.http.client.reactive;
1818

19-
import java.lang.reflect.Method;
2019
import java.net.HttpCookie;
2120
import java.util.List;
2221
import java.util.regex.Matcher;
2322
import java.util.regex.Pattern;
2423

25-
import org.eclipse.jetty.client.api.Response;
2624
import org.eclipse.jetty.reactive.client.ReactiveResponse;
2725
import org.reactivestreams.Publisher;
2826
import reactor.core.publisher.Flux;
@@ -32,11 +30,9 @@
3230
import org.springframework.http.HttpStatus;
3331
import org.springframework.http.ResponseCookie;
3432
import org.springframework.lang.Nullable;
35-
import org.springframework.util.Assert;
3633
import org.springframework.util.CollectionUtils;
3734
import org.springframework.util.LinkedMultiValueMap;
3835
import org.springframework.util.MultiValueMap;
39-
import org.springframework.util.ReflectionUtils;
4036

4137
/**
4238
* {@link ClientHttpResponse} implementation for the Jetty ReactiveStreams HTTP client.
@@ -50,10 +46,6 @@ class JettyClientHttpResponse implements ClientHttpResponse {
5046

5147
private static final Pattern SAMESITE_PATTERN = Pattern.compile("(?i).*SameSite=(Strict|Lax|None).*");
5248

53-
private static final ClassLoader classLoader = JettyClientHttpResponse.class.getClassLoader();
54-
55-
private static final boolean jetty10Present;
56-
5749

5850
private final ReactiveResponse reactiveResponse;
5951

@@ -62,23 +54,12 @@ class JettyClientHttpResponse implements ClientHttpResponse {
6254
private final HttpHeaders headers;
6355

6456

65-
static {
66-
try {
67-
Class<?> httpFieldsClass = classLoader.loadClass("org.eclipse.jetty.http.HttpFields");
68-
jetty10Present = httpFieldsClass.isInterface();
69-
}
70-
catch (ClassNotFoundException ex) {
71-
throw new IllegalStateException("No compatible Jetty version found", ex);
72-
}
73-
}
74-
75-
7657
public JettyClientHttpResponse(ReactiveResponse reactiveResponse, Publisher<DataBuffer> content) {
7758
this.reactiveResponse = reactiveResponse;
7859
this.content = Flux.from(content);
7960

80-
MultiValueMap<String, String> headers = (jetty10Present ?
81-
Jetty10HttpFieldsHelper.getHttpHeaders(reactiveResponse) :
61+
MultiValueMap<String, String> headers = (Jetty10HttpFieldsHelper.jetty10Present() ?
62+
Jetty10HttpFieldsHelper.getHttpHeaders(reactiveResponse.getResponse()) :
8263
new JettyHeadersAdapter(reactiveResponse.getHeaders()));
8364

8465
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
@@ -132,40 +113,4 @@ public HttpHeaders getHeaders() {
132113
return this.headers;
133114
}
134115

135-
136-
private static class Jetty10HttpFieldsHelper {
137-
138-
private static final Method getHeadersMethod;
139-
140-
private static final Method getNameMethod;
141-
142-
private static final Method getValueMethod;
143-
144-
static {
145-
try {
146-
getHeadersMethod = Response.class.getMethod("getHeaders");
147-
Class<?> type = classLoader.loadClass("org.eclipse.jetty.http.HttpField");
148-
getNameMethod = type.getMethod("getName");
149-
getValueMethod = type.getMethod("getValue");
150-
}
151-
catch (ClassNotFoundException | NoSuchMethodException ex) {
152-
throw new IllegalStateException("No compatible Jetty version found", ex);
153-
}
154-
}
155-
156-
public static HttpHeaders getHttpHeaders(ReactiveResponse response) {
157-
HttpHeaders headers = new HttpHeaders();
158-
Iterable<?> iterator = (Iterable<?>)
159-
ReflectionUtils.invokeMethod(getHeadersMethod, response.getResponse());
160-
Assert.notNull(iterator, "Iterator must not be null");
161-
for (Object field : iterator) {
162-
String name = (String) ReflectionUtils.invokeMethod(getNameMethod, field);
163-
Assert.notNull(name, "Header name must not be null");
164-
String value = (String) ReflectionUtils.invokeMethod(getValueMethod, field);
165-
headers.add(name, value);
166-
}
167-
return headers;
168-
}
169-
}
170-
171116
}

0 commit comments

Comments
 (0)