From dc351234767be1d62c6f4a5b23296c06c1233a8a Mon Sep 17 00:00:00 2001 From: Maxim Nesen Date: Wed, 9 Aug 2023 15:06:34 +0200 Subject: [PATCH] HTTP/2 for JNH connector Signed-off-by: Maxim Nesen --- .../JavaNetHttpClientProperties.java | 11 +++ .../jnh/connector/JavaNetHttpConnector.java | 9 +- .../jnh/connector/Http2PresenceTest.java | 89 +++++++++++++++++++ docs/src/main/docbook/appendix-properties.xml | 18 ++++ docs/src/main/docbook/jersey.ent | 1 + 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/Http2PresenceTest.java diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java index a964200018..a9c0248aa0 100644 --- a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java +++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java @@ -85,6 +85,17 @@ public class JavaNetHttpClientProperties { public static final String DISABLE_COOKIES = "jersey.config.jnh.client.disableCookies"; + /** + * HTTP version - if null or instance of HttpClient.Version.HTTP_1_1 the version will be set to HTTP_1_1 + * if version is HttpClient.Version.HTTP_2 the client will attempt to perform each request using HTTP_2 protocol + * but if not supported by server, the protocol will be still HTTP_1_1 + * + * @since 3.1.4 + */ + public static final String HTTP_VERSION = + "jersey.config.jnh.client.httpVersion"; + + /** * Prevent this class from instantiation. */ diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java index fb0f6131b8..69d25eb66f 100644 --- a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java +++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java @@ -83,8 +83,10 @@ public class JavaNetHttpConnector implements Connector { * @param configuration the configuration properties for this connector */ public JavaNetHttpConnector(final Client client, final Configuration configuration) { - HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); - httpClientBuilder.version(HttpClient.Version.HTTP_1_1); + final HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); + final HttpClient.Version version = + getPropertyOrNull(configuration, JavaNetHttpClientProperties.HTTP_VERSION, HttpClient.Version.class); + httpClientBuilder.version(version == null ? HttpClient.Version.HTTP_1_1 : version); SSLContext sslContext = client.getSslContext(); if (sslContext != null) { httpClientBuilder.sslContext(sslContext); @@ -230,6 +232,9 @@ private T getPropertyOrNull(final Configuration configuration, final String if (propertyObject == null) { return null; } + if (resultClass.isEnum() && propertyObject instanceof String) { + return (T) Enum.valueOf(resultClass.asSubclass(Enum.class), (String) propertyObject); + } if (!resultClass.isInstance(propertyObject)) { LOGGER.warning(LocalizationMessages.ERROR_INVALID_CLASS(propertyKey, resultClass.getName())); return null; diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/Http2PresenceTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/Http2PresenceTest.java new file mode 100644 index 0000000000..a2eaa3beea --- /dev/null +++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/Http2PresenceTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.jnh.connector; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.spi.ConnectorProvider; +import org.glassfish.jersey.logging.LoggingFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.jupiter.api.Test; + +import java.net.http.HttpClient; +import java.util.List; +import java.util.logging.Logger; + +import static java.net.http.HttpClient.Version.HTTP_2; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests the HTTP2 presence. + * + */ +public class Http2PresenceTest extends JerseyTest { + + private static final Logger LOGGER = Logger.getLogger(Http2PresenceTest.class.getName()); + + @Path("/test") + public static class HttpMethodResource { + + @GET + public String testUserAgent(@Context HttpHeaders httpHeaders) { + final List requestHeader = httpHeaders.getRequestHeader(HttpHeaders.USER_AGENT); + if (requestHeader.size() != 1) { + return "FAIL"; + } + return requestHeader.get(0); + } + } + + @Override + protected Application configure() { + ResourceConfig config = new ResourceConfig(HttpMethodResource.class); + config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY)); + return config; + } + + @Override + protected void configureClient(ClientConfig config) { + config.property(JavaNetHttpClientProperties.HTTP_VERSION, HTTP_2).connectorProvider(new JavaNetHttpConnectorProvider()); + } + + @Test + public void testHttp2Presence() { + final ConnectorProvider provider = ((ClientConfig) target().getConfiguration()).getConnectorProvider(); + assertTrue(provider instanceof JavaNetHttpConnectorProvider); + + final HttpClient client = ((JavaNetHttpConnectorProvider) provider).getHttpClient(target()); + assertEquals(HTTP_2, client.version()); + } + + /** + * Test, that {@code User-agent} header is as set by Jersey, not by underlying Jetty client. + */ + @Test + public void testUserAgent() { + String response = target().path("test").request().get(String.class); + assertTrue(response.startsWith("Jersey"), "User-agent header should start with 'Jersey', but was " + response); + } +} \ No newline at end of file diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml index d8dad09831..78bfb962ad 100644 --- a/docs/src/main/docbook/appendix-properties.xml +++ b/docs/src/main/docbook/appendix-properties.xml @@ -2119,6 +2119,24 @@ + + &jersey.jnh.JavaNetHttpClientProperties.HTTP_VERSION; + jersey.config.jnh.client.httpVersion + + + HTTP version - if null or instance of HttpClient.Version.HTTP_1_1 + the version will be set to HTTP_1_1. If version is HttpClient.Version.HTTP_2 + the client will attempt to perform each request using HTTP_2 protocol + but if not supported by server, the protocol will be still HTTP_1_1 + + + The value MUST be an instance of java.net.http.HttpClient.Version. + + + The default value is &lit.null;. + + + diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent index 4a81897979..ab13afb87e 100644 --- a/docs/src/main/docbook/jersey.ent +++ b/docs/src/main/docbook/jersey.ent @@ -493,6 +493,7 @@ JavaNetHttpClientProperties.SSL_PARAMETERS"> JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION"> JavaNetHttpClientProperties.DISABLE_COOKIES"> +JavaNetHttpClientProperties.HTTP_VERSION"> DeclarativeLinkingFeature"> LoggingFeature"> LoggingFeature.DEFAULT_LOGGER_NAME">