Skip to content

Commit

Permalink
Support Jetty 10 in JettyClientHttpRequest
Browse files Browse the repository at this point in the history
Though Jetty 10 was previously supported in the JettyClientHttpResponse,
this commit ensures support in the JettyClientHttpRequest.

Closes gh-29867
See gh-26123
  • Loading branch information
poutsma committed Jan 26, 2023
1 parent de53d77 commit 21c3d4f
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2002-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.http.client.reactive;

import java.lang.reflect.Method;

import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;

import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

/**
* Used to support Jetty 10.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @since 5.3.26
*/
abstract class Jetty10HttpFieldsHelper {

private static final boolean jetty10Present;

private static final Method requestGetHeadersMethod;

private static final Method responseGetHeadersMethod;

private static final Method getNameMethod;

private static final Method getValueMethod;


static {
try {
ClassLoader classLoader = JettyClientHttpResponse.class.getClassLoader();
Class<?> httpFieldsClass = classLoader.loadClass("org.eclipse.jetty.http.HttpFields");
jetty10Present = httpFieldsClass.isInterface();
requestGetHeadersMethod = Request.class.getMethod("getHeaders");
responseGetHeadersMethod = Response.class.getMethod("getHeaders");
Class<?> httpFieldClass = classLoader.loadClass("org.eclipse.jetty.http.HttpField");
getNameMethod = httpFieldClass.getMethod("getName");
getValueMethod = httpFieldClass.getMethod("getValue");
}
catch (ClassNotFoundException | NoSuchMethodException ex) {
throw new IllegalStateException("No compatible Jetty version found", ex);
}
}


public static boolean jetty10Present() {
return jetty10Present;
}

public static HttpHeaders getHttpHeaders(Request request) {
Iterable<?> iterator = (Iterable<?>)
ReflectionUtils.invokeMethod(requestGetHeadersMethod, request);
return getHttpHeadersInternal(iterator);
}

public static HttpHeaders getHttpHeaders(Response response) {
Iterable<?> iterator = (Iterable<?>)
ReflectionUtils.invokeMethod(responseGetHeadersMethod, response);
return getHttpHeadersInternal(iterator);
}

private static HttpHeaders getHttpHeadersInternal(@Nullable Iterable<?> iterator) {
Assert.notNull(iterator, "Iterator must not be null");
HttpHeaders headers = new HttpHeaders();
for (Object field : iterator) {
String name = (String) ReflectionUtils.invokeMethod(getNameMethod, field);
Assert.notNull(name, "Header name must not be null");
String value = (String) ReflectionUtils.invokeMethod(getValueMethod, field);
headers.add(name, value);
}
return headers;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -37,6 +37,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap;

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



public JettyClientHttpRequest(Request jettyRequest, DataBufferFactory bufferFactory) {
this.jettyRequest = jettyRequest;
this.bufferFactory = bufferFactory;
Expand Down Expand Up @@ -144,7 +144,11 @@ protected void applyHeaders() {

@Override
protected HttpHeaders initReadOnlyHeaders() {
return HttpHeaders.readOnlyHttpHeaders(new JettyHeadersAdapter(this.jettyRequest.getHeaders()));
MultiValueMap<String, String> headers = (Jetty10HttpFieldsHelper.jetty10Present() ?
Jetty10HttpFieldsHelper.getHttpHeaders(this.jettyRequest) :
new JettyHeadersAdapter(this.jettyRequest.getHeaders()));

return HttpHeaders.readOnlyHttpHeaders(headers);
}

public ReactiveRequest toReactiveRequest() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
Expand All @@ -16,13 +16,11 @@

package org.springframework.http.client.reactive;

import java.lang.reflect.Method;
import java.net.HttpCookie;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.reactive.client.ReactiveResponse;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
Expand All @@ -32,11 +30,9 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;

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

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

private static final ClassLoader classLoader = JettyClientHttpResponse.class.getClassLoader();

private static final boolean jetty10Present;


private final ReactiveResponse reactiveResponse;

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


static {
try {
Class<?> httpFieldsClass = classLoader.loadClass("org.eclipse.jetty.http.HttpFields");
jetty10Present = httpFieldsClass.isInterface();
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("No compatible Jetty version found", ex);
}
}


public JettyClientHttpResponse(ReactiveResponse reactiveResponse, Publisher<DataBuffer> content) {
this.reactiveResponse = reactiveResponse;
this.content = Flux.from(content);

MultiValueMap<String, String> headers = (jetty10Present ?
Jetty10HttpFieldsHelper.getHttpHeaders(reactiveResponse) :
MultiValueMap<String, String> headers = (Jetty10HttpFieldsHelper.jetty10Present() ?
Jetty10HttpFieldsHelper.getHttpHeaders(reactiveResponse.getResponse()) :
new JettyHeadersAdapter(reactiveResponse.getHeaders()));

this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
Expand Down Expand Up @@ -132,40 +113,4 @@ public HttpHeaders getHeaders() {
return this.headers;
}


private static class Jetty10HttpFieldsHelper {

private static final Method getHeadersMethod;

private static final Method getNameMethod;

private static final Method getValueMethod;

static {
try {
getHeadersMethod = Response.class.getMethod("getHeaders");
Class<?> type = classLoader.loadClass("org.eclipse.jetty.http.HttpField");
getNameMethod = type.getMethod("getName");
getValueMethod = type.getMethod("getValue");
}
catch (ClassNotFoundException | NoSuchMethodException ex) {
throw new IllegalStateException("No compatible Jetty version found", ex);
}
}

public static HttpHeaders getHttpHeaders(ReactiveResponse response) {
HttpHeaders headers = new HttpHeaders();
Iterable<?> iterator = (Iterable<?>)
ReflectionUtils.invokeMethod(getHeadersMethod, response.getResponse());
Assert.notNull(iterator, "Iterator must not be null");
for (Object field : iterator) {
String name = (String) ReflectionUtils.invokeMethod(getNameMethod, field);
Assert.notNull(name, "Header name must not be null");
String value = (String) ReflectionUtils.invokeMethod(getValueMethod, field);
headers.add(name, value);
}
return headers;
}
}

}

0 comments on commit 21c3d4f

Please sign in to comment.