From 989fcd6ddfee32bfa56b65489514708acb5b0b8b Mon Sep 17 00:00:00 2001 From: Maxim Nesen Date: Tue, 28 Mar 2023 14:58:32 +0200 Subject: [PATCH] HTTP/2 for Jetty container Signed-off-by: Maxim Nesen --- bom/pom.xml | 5 + .../jersey/jetty/JettyHttpContainer.java | 2 +- .../jetty/JettyHttpContainerProvider.java | 10 +- containers/jetty-http2/pom.xml | 112 +++++++++ .../http2/JettyHttp2ContainerFactory.java | 217 ++++++++++++++++++ .../http2/JettyHttp2ContainerProvider.java | 38 +++ .../jersey/jetty/http2/package-info.java | 20 ++ ...ssfish.jersey.server.spi.ContainerProvider | 1 + .../jetty/http2/localization.properties | 18 ++ containers/pom.xml | 1 + pom.xml | 10 + test-framework/providers/jetty-http2/pom.xml | 47 ++++ .../http2/JettyHttp2TestContainerFactory.java | 126 ++++++++++ .../jersey/test/jetty/http2/package-info.java | 20 ++ ...sfish.jersey.test.spi.TestContainerFactory | 1 + .../jetty/http2/AvailablePortJettyTest.java | 67 ++++++ .../test/jetty/http2/JettyContainerTest.java | 119 ++++++++++ test-framework/providers/pom.xml | 1 + 18 files changed, 813 insertions(+), 2 deletions(-) create mode 100644 containers/jetty-http2/pom.xml create mode 100644 containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java create mode 100644 containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerProvider.java create mode 100644 containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java create mode 100644 containers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider create mode 100644 containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties create mode 100644 test-framework/providers/jetty-http2/pom.xml create mode 100644 test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java create mode 100644 test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java create mode 100644 test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory create mode 100644 test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java create mode 100644 test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/JettyContainerTest.java diff --git a/bom/pom.xml b/bom/pom.xml index 9131a686c2..3864b913a3 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -83,6 +83,11 @@ jersey-jetty-connector ${project.version} + + org.glassfish.jersey.connectors + jersey-jetty-http2-connector + ${project.version} + org.glassfish.jersey.connectors jersey-jdk-connector diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java index 78d16058c1..8a5366e141 100644 --- a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java +++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java index 7c50c759b7..289b022eef 100644 --- a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java +++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -39,4 +39,12 @@ public T createContainer(final Class type, final Application application) return null; } + public T createContainer(final Class type, final Application application, + Object parentContext) throws ProcessingException { + if (Handler.class == type || JettyHttpContainer.class == type) { + return type.cast(new JettyHttpContainer(application, parentContext)); + } + return null; + } + } diff --git a/containers/jetty-http2/pom.xml b/containers/jetty-http2/pom.xml new file mode 100644 index 0000000000..503d6ec780 --- /dev/null +++ b/containers/jetty-http2/pom.xml @@ -0,0 +1,112 @@ + + + + + 4.0.0 + + + project + org.glassfish.jersey.containers + 2.40-SNAPSHOT + + + jersey-container-jetty-http2 + jar + jersey-container-jetty-http2 + + Jetty Http2 Container + + + + org.glassfish.jersey.containers + jersey-container-jetty-http + ${project.version} + + + org.glassfish.hk2.external + jakarta.inject + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-util + + + org.eclipse.jetty + jetty-continuation + + + org.eclipse.jetty.http2 + http2-server + + + org.eclipse.jetty + jetty-alpn-conscrypt-server + + + org.apache.httpcomponents + httpclient + test + + + org.hamcrest + hamcrest + test + + + + + + + com.sun.istack + istack-commons-maven-plugin + true + + + org.codehaus.mojo + build-helper-maven-plugin + true + + + org.apache.felix + maven-bundle-plugin + true + + + + ${jetty.osgi.version}, + * + + + + + + + + + ${basedir}/src/main/resources + true + + + + + diff --git a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java new file mode 100644 index 0000000000..8871e4c929 --- /dev/null +++ b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java @@ -0,0 +1,217 @@ +/* + * 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.jetty.http2; + +import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; +import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; +import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.glassfish.jersey.jetty.JettyHttpContainer; +import org.glassfish.jersey.jetty.JettyHttpContainerFactory; +import org.glassfish.jersey.jetty.JettyHttpContainerProvider; +import org.glassfish.jersey.jetty.http2.LocalizationMessages; +import org.glassfish.jersey.server.ContainerFactory; +import org.glassfish.jersey.server.ResourceConfig; + +import javax.ws.rs.ProcessingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public final class JettyHttp2ContainerFactory { + + private JettyHttp2ContainerFactory() { + + } + + /** + * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}. + * + * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path + * segment will be used as context path, the rest will be ignored. + * @return newly created {@link Server}. + * + * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance. + * @throws IllegalArgumentException if {@code uri} is {@code null}. + */ + public static Server createHttp2Server(final URI uri) throws ProcessingException { + return createHttp2Server(uri, null, null, true); + } + + /** + * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that + * in turn manages all root resource and provider classes declared by the + * resource configuration. + *

+ * This implementation defers to the + * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, javax.ws.rs.core.Application)} method + * for creating an Container that manages the root resources. + * + * @param uri URI on which the Jersey web application will be deployed. Only first path segment will be + * used as context path, the rest will be ignored. + * @param configuration web application configuration. + * @param start if set to false, server will not get started, which allows to configure the underlying + * transport layer, see above for details. + * @return newly created {@link Server}. + * + * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance. + * @throws IllegalArgumentException if {@code uri} is {@code null}. + */ + public static Server createHttp2Server(final URI uri, final ResourceConfig configuration, final boolean start) + throws ProcessingException { + return createHttp2Server(uri, null, + ContainerFactory.createContainer(JettyHttpContainer.class, configuration), start); + } + + /** + * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}. + * + * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path + * segment will be used as context path, the rest will be ignored. + * @param start if set to false, server will not get started, which allows to configure the underlying transport + * layer, see above for details. + * @return newly created {@link Server}. + * + * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance. + * @throws IllegalArgumentException if {@code uri} is {@code null}. + * + * @since 2.40 + */ + + public static Server createHttp2Server(final URI uri, final boolean start) throws ProcessingException { + return createHttp2Server(uri, null, null, start); + } + + /** + * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that + * in turn manages all root resource and provider classes declared by the + * resource configuration. + * + * @param uri the URI to create the http server. The URI scheme must be + * equal to "https". The URI user information and host + * are ignored If the URI port is not present then port 143 will be + * used. The URI path, query and fragment components are ignored. + * @param config the resource configuration. + * @param parentContext DI provider specific context with application's registered bindings. + * @param start if set to false, server will not get started, this allows end users to set + * additional properties on the underlying listener. + * @return newly created {@link Server}. + * + * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance. + * @throws IllegalArgumentException if {@code uri} is {@code null}. + * @see JettyHttpContainer + * + * @since 2.40 + */ + public static Server createHttp2Server(final URI uri, final ResourceConfig config, final boolean start, + final Object parentContext) { + return createHttp2Server(uri, null, + new JettyHttpContainerProvider().createContainer(JettyHttpContainer.class, + config, parentContext), start); + } + + /** + * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that + * in turn manages all root resource and provider classes found by searching the + * classes referenced in the java classpath. + * + * @param uri the URI to create the http server. The URI scheme must be + * equal to {@code https}. The URI user information and host + * are ignored. If the URI port is not present then port + * {@value org.glassfish.jersey.server.spi.Container#DEFAULT_HTTPS_PORT} will be + * used. The URI path, query and fragment components are ignored. + * @param sslContextFactory this is the SSL context factory used to configure SSL connector + * @param handler the container that handles all HTTP requests + * @param start if set to false, server will not get started, this allows end users to set + * additional properties on the underlying listener. + * @return newly created {@link Server}. + * + * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance. + * @throws IllegalArgumentException if {@code uri} is {@code null}. + * @see JettyHttpContainer + * + * @since 2.40 + */ + public static Server createHttp2Server(final URI uri, + final SslContextFactory sslContextFactory, + final JettyHttpContainer handler, + final boolean start) { + + /** + * Creating basic Jetty HTTP/1.1 container (but always not started) + */ + final Server server = JettyHttpContainerFactory.createServer(uri, sslContextFactory, handler, false); + /** + * Obtain configured HTTP connection factory + */ + final ServerConnector httpServerConnector = (ServerConnector) server.getConnectors()[0]; + final HttpConnectionFactory httpConnectionFactory = httpServerConnector.getConnectionFactory(HttpConnectionFactory.class); + + /** + * Obtain prepared config + */ + final HttpConfiguration config = httpConnectionFactory.getHttpConfiguration(); + + /** + * Add required H2/H2C connection factories using pre-configured config from the HTTP/1.1 server + */ + final List factories = getConnectionFactories(config, sslContextFactory); + factories.add(httpConnectionFactory); + + /** + * replacing connectors for H2/H2C protocol + */ + httpServerConnector.setConnectionFactories(factories); + server.setConnectors(new Connector[]{httpServerConnector}); + + /** + * Starting the server if required + */ + if (start) { + try { + // Start the server. + server.start(); + } catch (final Exception e) { + throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e); + } + } + return server; + } + + private static List getConnectionFactories(final HttpConfiguration config, + final SslContextFactory sslContextFactory) { + final List factories = new ArrayList<>(); + if (sslContextFactory != null) { + factories.add(new HTTP2ServerConnectionFactory(config)); + final ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory(); + alpn.setDefaultProtocol("h2"); + factories.add(new SslConnectionFactory(sslContextFactory, alpn.getProtocol())); + factories.add(alpn); + } else { + factories.add(new HTTP2CServerConnectionFactory(config)); + } + + return factories; + } +} diff --git a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerProvider.java b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerProvider.java new file mode 100644 index 0000000000..166f02d048 --- /dev/null +++ b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerProvider.java @@ -0,0 +1,38 @@ +/* + * 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.jetty.http2; + +import org.eclipse.jetty.server.Handler; +import org.glassfish.jersey.jetty.JettyHttpContainer; +import org.glassfish.jersey.jetty.JettyHttpContainerProvider; +import org.glassfish.jersey.server.spi.ContainerProvider; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.core.Application; + +public final class JettyHttp2ContainerProvider implements ContainerProvider { + + @Override + public T createContainer(final Class type, final Application application) throws ProcessingException { + if (Handler.class == type || JettyHttpContainer.class == type) { + return type.cast(new JettyHttpContainerProvider().createContainer(JettyHttpContainer.class, + application)); + } + return null; + } +} + diff --git a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java new file mode 100644 index 0000000000..a402b27115 --- /dev/null +++ b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java @@ -0,0 +1,20 @@ +/* + * 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 + */ + +/** + * Jersey Jetty HTTP2 container classes. + */ +package org.glassfish.jersey.jetty.http2; diff --git a/containers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider b/containers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider new file mode 100644 index 0000000000..4d2a88dd12 --- /dev/null +++ b/containers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider @@ -0,0 +1 @@ +org.glassfish.jersey.jetty.http2.JettyHttp2ContainerProvider \ No newline at end of file diff --git a/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties b/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties new file mode 100644 index 0000000000..9728b70270 --- /dev/null +++ b/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties @@ -0,0 +1,18 @@ +# +# 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 +# + +# {0} - status code; {1} - status reason message +error.when.creating.server=Exception thrown when trying to create jetty server. \ No newline at end of file diff --git a/containers/pom.xml b/containers/pom.xml index 37c90d51a6..1fd172034f 100644 --- a/containers/pom.xml +++ b/containers/pom.xml @@ -41,6 +41,7 @@ jersey-servlet-core jersey-servlet jetty-http + jetty-http2 jetty-servlet netty-http simple-http diff --git a/pom.xml b/pom.xml index a4bfb4f87f..d1d4c23a78 100644 --- a/pom.xml +++ b/pom.xml @@ -1720,6 +1720,16 @@ jetty-continuation ${jetty.version} + + org.eclipse.jetty.http2 + http2-server + ${jetty.version} + + + org.eclipse.jetty + jetty-alpn-conscrypt-server + ${jetty.version} + org.simpleframework diff --git a/test-framework/providers/jetty-http2/pom.xml b/test-framework/providers/jetty-http2/pom.xml new file mode 100644 index 0000000000..afb4eb54c1 --- /dev/null +++ b/test-framework/providers/jetty-http2/pom.xml @@ -0,0 +1,47 @@ + + + + + + project + org.glassfish.jersey.test-framework.providers + 2.40-SNAPSHOT + + 4.0.0 + + jersey-test-framework-provider-jetty-http2 + jar + jersey-test-framework-provider-jetty-http2 + + Jersey Test Framework - Jetty HTTP2 container + + + + org.glassfish.jersey.test-framework + jersey-test-framework-core + ${project.version} + + + org.glassfish.jersey.containers + jersey-container-jetty-http2 + ${project.version} + + + + diff --git a/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java new file mode 100644 index 0000000000..85a673c8bc --- /dev/null +++ b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java @@ -0,0 +1,126 @@ +/* + * 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.test.jetty.http2; + +import java.net.URI; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.core.UriBuilder; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.jetty.http2.JettyHttp2ContainerFactory; +import org.glassfish.jersey.test.DeploymentContext; +import org.glassfish.jersey.test.spi.TestContainer; +import org.glassfish.jersey.test.spi.TestContainerException; +import org.glassfish.jersey.test.spi.TestContainerFactory; +import org.glassfish.jersey.test.spi.TestHelper; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; + +/** + * Factory for testing {@link JettyHttp2ContainerFactory}. + * + */ +public final class JettyHttp2TestContainerFactory implements TestContainerFactory { + + private static class JettyHttp2TestContainer implements TestContainer { + + private static final Logger LOGGER = Logger.getLogger(JettyHttp2TestContainer.class.getName()); + + private URI baseUri; + private final Server server; + + private JettyHttp2TestContainer(final URI baseUri, final DeploymentContext context) { + final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build(); + + if (!"/".equals(base.getRawPath())) { + throw new TestContainerException(String.format( + "Cannot deploy on %s. Jetty HTTP2 container only supports deployment on root path.", + base.getRawPath())); + } + + this.baseUri = base; + + if (LOGGER.isLoggable(Level.INFO)) { + LOGGER.info("Creating JettyHttp2TestContainer configured at the base URI " + + TestHelper.zeroPortToAvailablePort(baseUri)); + } + + this.server = JettyHttp2ContainerFactory.createHttp2Server(this.baseUri, context.getResourceConfig(), false); + } + + @Override + public ClientConfig getClientConfig() { + return null; + } + + @Override + public URI getBaseUri() { + return baseUri; + } + + @Override + public void start() { + if (server.isStarted()) { + LOGGER.log(Level.WARNING, "Ignoring start request - JettyHttp2TestContainer is already started."); + } else { + LOGGER.log(Level.FINE, "Starting JettyHttp2TestContainer..."); + try { + server.start(); + + if (baseUri.getPort() == 0) { + int port = 0; + for (final Connector connector : server.getConnectors()) { + if (connector instanceof ServerConnector) { + port = ((ServerConnector) connector).getLocalPort(); + break; + } + } + + baseUri = UriBuilder.fromUri(baseUri).port(port).build(); + + LOGGER.log(Level.INFO, "Started JettyHttp2TestContainer at the base URI " + baseUri); + } + } catch (Exception e) { + throw new TestContainerException(e); + } + } + } + + @Override + public void stop() { + if (server.isStarted()) { + LOGGER.log(Level.FINE, "Stopping JettyHttp2TestContainer..."); + try { + this.server.stop(); + } catch (Exception ex) { + LOGGER.log(Level.WARNING, "Error Stopping JettyHttp2TestContainer...", ex); + } + } else { + LOGGER.log(Level.WARNING, "Ignoring stop request - JettyHttp2TestContainer is already stopped."); + } + } + } + + @Override + public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException { + return new JettyHttp2TestContainer(baseUri, context); + } +} diff --git a/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java new file mode 100644 index 0000000000..cd4980f3f4 --- /dev/null +++ b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java @@ -0,0 +1,20 @@ +/* + * 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 + */ + +/** + * Jersey test framework for Jetty HTTP/2 Container. + */ +package org.glassfish.jersey.test.jetty.http2; diff --git a/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory b/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory new file mode 100644 index 0000000000..42b7847026 --- /dev/null +++ b/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory @@ -0,0 +1 @@ +org.glassfish.jersey.test.jetty.http2.JettyHttp2TestContainerFactory \ No newline at end of file diff --git a/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java new file mode 100644 index 0000000000..7e048e7514 --- /dev/null +++ b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java @@ -0,0 +1,67 @@ +/* + * 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.test.jetty.http2; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.DeploymentContext; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.glassfish.jersey.test.jetty.http2.JettyHttp2TestContainerFactory; +import org.glassfish.jersey.test.spi.TestContainerFactory; + +import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests finding an available port for container. + * + */ +public class AvailablePortJettyTest extends JerseyTest { + + @Override + protected TestContainerFactory getTestContainerFactory() { + return new JettyHttp2TestContainerFactory(); + } + + @Path("AvailablePortJettyTest") + public static class TestResource { + @GET + public String get() { + return "GET"; + } + } + + @Override + protected DeploymentContext configureDeployment() { + forceSet(TestProperties.CONTAINER_PORT, "0"); + + return DeploymentContext.builder(new ResourceConfig(TestResource.class)).build(); + } + + @Test + public void testGet() { + assertThat(target().getUri().getPort(), not(0)); + assertThat(getBaseUri().getPort(), not(0)); + + assertThat(target("AvailablePortJettyTest").request().get(String.class), equalTo("GET")); + } +} diff --git a/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/JettyContainerTest.java b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/JettyContainerTest.java new file mode 100644 index 0000000000..16072f6f55 --- /dev/null +++ b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/JettyContainerTest.java @@ -0,0 +1,119 @@ +/* + * 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.test.jetty.http2; + +import java.net.URI; +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import org.glassfish.jersey.inject.hk2.DelayedHk2InjectionManager; +import org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.jetty.http2.JettyHttp2ContainerFactory; +import org.glassfish.jersey.jetty.JettyHttpContainer; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; + +import org.glassfish.hk2.api.ServiceLocator; + +import org.jvnet.hk2.internal.ServiceLocatorImpl; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test class for {@link JettyHttpContainer}. + * + */ +public class JettyContainerTest extends JerseyTest { + + /** + * Creates new instance. + */ + public JettyContainerTest() { + super(new JettyHttp2TestContainerFactory()); + } + + @Override + protected ResourceConfig configure() { + return new ResourceConfig(Resource.class); + } + + /** + * Test resource class. + */ + @Path("one") + public static class Resource { + + /** + * Test resource method. + * + * @return Test simple string response. + */ + @GET + public String getSomething() { + return "get"; + } + } + + @Test + /** + * Test {@link Server Jetty Server} container. + */ + public void testJettyContainerTarget() { + final Response response = target().path("one").request().get(); + + assertEquals(200, response.getStatus(), "Response status unexpected."); + assertEquals("get", response.readEntity(String.class), "Response entity unexpected."); + } + + /** + * Test that defined ServiceLocator becomes a parent of the newly created service locator. + */ + @Test + public void testParentServiceLocator() { + final ServiceLocator locator = new ServiceLocatorImpl("MyServiceLocator", null); + final Server server = JettyHttp2ContainerFactory.createHttp2Server(URI.create("http://localhost:9876"), + new ResourceConfig(Resource.class), false, locator); + final JettyHttpContainer container = (JettyHttpContainer) server.getHandler(); + final InjectionManager injectionManager = container.getApplicationHandler().getInjectionManager(); + + ServiceLocator serviceLocator; + if (injectionManager instanceof ImmediateHk2InjectionManager) { + serviceLocator = ((ImmediateHk2InjectionManager) injectionManager).getServiceLocator(); + } else if (injectionManager instanceof DelayedHk2InjectionManager) { + serviceLocator = ((DelayedHk2InjectionManager) injectionManager).getServiceLocator(); + } else { + throw new RuntimeException("Invalid Hk2 InjectionManager"); + } + assertTrue(serviceLocator.getParent() == locator, + "Application injection manager was expected to have defined parent locator"); + } + @Test + public void testHttp2Container() { + final ServiceLocator locator = new ServiceLocatorImpl("MyServiceLocator", null); + final Server server = JettyHttp2ContainerFactory.createHttp2Server(URI.create("http://localhost:9876"), + new ResourceConfig(Resource.class), true, locator); + final List protocols = server.getConnectors()[0].getProtocols(); + assertTrue(protocols.contains("h2") || protocols.contains("h2c")); + } +} diff --git a/test-framework/providers/pom.xml b/test-framework/providers/pom.xml index 2fd0fa1746..3d22043a18 100644 --- a/test-framework/providers/pom.xml +++ b/test-framework/providers/pom.xml @@ -38,6 +38,7 @@ grizzly2 inmemory jetty + jetty-http2 jdk-http netty simple