From bb52d95a62919491c980f7e17953076e6533b336 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Fri, 23 Aug 2024 17:33:51 +0300 Subject: [PATCH] Fixes #12063 - Introduce Jetty module for HTTP/2 client dependencies. (#12170) Introduced http2-client.mod and http2-client-transport.mod. These modules download dependencies via a [files] section. They can be used to have the server provide the dependencies in case of a web application proxies request using HTTP/2. Fixed ContentProvider to set the context ClassLoader before reading the Jetty XML context file, which may reference classes from the web application. Signed-off-by: Simone Bordet --- .../src/main/config/modules/client.mod | 8 ++- .../deploy/providers/ContextProvider.java | 6 +- .../config/modules/http2-client-transport.mod | 16 +++++ .../src/main/config/modules/http2-client.mod | 18 ++++++ .../pom.xml | 30 ++++++++++ .../HTTP2ClientTransportProvidedHandler.java | 59 +++++++++++++++++++ jetty-core/jetty-tests/pom.xml | 1 + jetty-home/pom.xml | 4 ++ .../tests/distribution/DistributionTests.java | 50 ++++++++++++++++ 9 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 jetty-core/jetty-http2/jetty-http2-client-transport/src/main/config/modules/http2-client-transport.mod create mode 100644 jetty-core/jetty-http2/jetty-http2-client/src/main/config/modules/http2-client.mod create mode 100644 jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/pom.xml create mode 100644 jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/src/main/java/org/eclipse/jetty/test/http2/client/transport/provided/HTTP2ClientTransportProvidedHandler.java diff --git a/jetty-core/jetty-client/src/main/config/modules/client.mod b/jetty-core/jetty-client/src/main/config/modules/client.mod index fa8da61d1821..c1dfd8bb6856 100644 --- a/jetty-core/jetty-client/src/main/config/modules/client.mod +++ b/jetty-core/jetty-client/src/main/config/modules/client.mod @@ -1,8 +1,12 @@ # DO NOT EDIT THIS FILE - See: https://jetty.org/docs/ [description] -Adds the Jetty HTTP client to the server classpath. +Adds the Jetty HTTP client dependencies to the server classpath. + +[tags] +client [lib] -lib/jetty-client-${jetty.version}.jar lib/jetty-alpn-client-${jetty.version}.jar +lib/jetty-alpn-java-client-${jetty.version}.jar +lib/jetty-client-${jetty.version}.jar diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 37e7f7f6ff19..f68a8fa353af 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -385,6 +385,10 @@ public ContextHandler createContextHandler(final App app) throws Exception // Handle a context XML file if (FileID.isXml(path)) { + ClassLoader coreContextClassLoader = Environment.CORE.equals(environment) ? findCoreContextClassLoader(path) : null; + if (coreContextClassLoader != null) + Thread.currentThread().setContextClassLoader(coreContextClassLoader); + context = applyXml(context, path, env, properties); // Look for the contextHandler itself @@ -401,13 +405,11 @@ else if (context instanceof Supplier supplier) throw new IllegalStateException("Unknown context type of " + context); // Set the classloader if we have a coreContextClassLoader - ClassLoader coreContextClassLoader = Environment.CORE.equals(environment) ? findCoreContextClassLoader(path) : null; if (coreContextClassLoader != null) contextHandler.setClassLoader(coreContextClassLoader); return contextHandler; } - // Otherwise it must be a directory or an archive else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) { diff --git a/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/config/modules/http2-client-transport.mod b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/config/modules/http2-client-transport.mod new file mode 100644 index 000000000000..bee6003c5334 --- /dev/null +++ b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/config/modules/http2-client-transport.mod @@ -0,0 +1,16 @@ +[description] +Adds the Jetty HTTP/2 client transport dependencies to the server classpath. + +[tags] +client +http2 + +[depends] +client +http2-client + +[files] +maven://org.eclipse.jetty.http2/jetty-http2-client-transport/${jetty.version}/jar|lib/http2/jetty-http2-client-transport-${jetty.version}.jar + +[lib] +lib/http2/jetty-http2-client-transport-${jetty.version}.jar diff --git a/jetty-core/jetty-http2/jetty-http2-client/src/main/config/modules/http2-client.mod b/jetty-core/jetty-http2/jetty-http2-client/src/main/config/modules/http2-client.mod new file mode 100644 index 000000000000..b8734cd52efb --- /dev/null +++ b/jetty-core/jetty-http2/jetty-http2-client/src/main/config/modules/http2-client.mod @@ -0,0 +1,18 @@ +[description] +Adds the Jetty HTTP/2 client dependencies to the server classpath. + +[tags] +client +http2 + +[files] +maven://org.eclipse.jetty/jetty-alpn-client/${jetty.version}/jar|lib/jetty-alpn-client-${jetty.version}.jar +maven://org.eclipse.jetty/jetty-alpn-java-client/${jetty.version}/jar|lib/jetty-alpn-java-client-${jetty.version}.jar +maven://org.eclipse.jetty.http2/jetty-http2-client/${jetty.version}/jar|lib/http2/jetty-http2-client-${jetty.version}.jar + +[lib] +lib/jetty-alpn-client-${jetty.version}.jar +lib/jetty-alpn-java-client-${jetty.version}.jar +lib/http2/jetty-http2-client-${jetty.version}.jar +lib/http2/jetty-http2-common-${jetty.version}.jar +lib/http2/jetty-http2-hpack-${jetty.version}.jar diff --git a/jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/pom.xml b/jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/pom.xml new file mode 100644 index 000000000000..5d01a9f84896 --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.eclipse.jetty + jetty-tests + 12.0.13-SNAPSHOT + + + jetty-test-http2-client-transport-provided-webapp + Core :: Tests :: HTTP/2 Client Transport Provided WebApp + + + true + + + + + org.eclipse.jetty + jetty-server + provided + + + org.eclipse.jetty.http2 + jetty-http2-client-transport + provided + + + + diff --git a/jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/src/main/java/org/eclipse/jetty/test/http2/client/transport/provided/HTTP2ClientTransportProvidedHandler.java b/jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/src/main/java/org/eclipse/jetty/test/http2/client/transport/provided/HTTP2ClientTransportProvidedHandler.java new file mode 100644 index 000000000000..7b50bde8b9f3 --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-http2-client-transport-provided-webapp/src/main/java/org/eclipse/jetty/test/http2/client/transport/provided/HTTP2ClientTransportProvidedHandler.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.test.http2.client.transport.provided; + +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http2.client.HTTP2Client; +import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +public class HTTP2ClientTransportProvidedHandler extends Handler.Abstract +{ + private final HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(new HTTP2Client())); + + @Override + protected void doStart() throws Exception + { + addBean(httpClient); + super.doStart(); + } + + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception + { + // Verify that the HTTP2Client dependencies are provided by the server + // by making a request to an external server, as if this Handler was a proxy. + + httpClient.newRequest("https://webtide.com/") + .timeout(15, TimeUnit.SECONDS) + .send(result -> + { + if (result.isSucceeded()) + { + response.setStatus(result.getResponse().getStatus()); + callback.succeeded(); + } + else + { + callback.failed(result.getFailure()); + } + }); + return true; + } +} diff --git a/jetty-core/jetty-tests/pom.xml b/jetty-core/jetty-tests/pom.xml index 4d84d2200c3f..52c44bf302bf 100644 --- a/jetty-core/jetty-tests/pom.xml +++ b/jetty-core/jetty-tests/pom.xml @@ -14,6 +14,7 @@ jetty-test-client-transports jetty-test-jmx + jetty-test-http2-client-transport-provided-webapp diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml index fc871896441c..cd1a56d1a9fd 100644 --- a/jetty-home/pom.xml +++ b/jetty-home/pom.xml @@ -158,6 +158,10 @@ jetty-gcloud-session-manager true + + org.eclipse.jetty.http2 + jetty-http2-client-transport + org.eclipse.jetty.http2 jetty-http2-server diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java index d1fa59a0afb3..7af0af814699 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -1892,4 +1892,54 @@ public void testStateTrackingModule() throws Exception } } } + + @Test + public void testHTTP2ClientInCoreWebAppProvidedByServer() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .build(); + + try (JettyHomeTester.Run run1 = distribution.start("--add-modules=http,http2-client-transport,core-deploy")) + { + assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + Path jettyLogging = distribution.getJettyBase().resolve("resources/jetty-logging.properties"); + String loggingConfig = """ + org.eclipse.jetty.LEVEL=DEBUG + """; + Files.writeString(jettyLogging, loggingConfig, StandardOpenOption.TRUNCATE_EXISTING); + + String name = "test-webapp"; + Path webapps = distribution.getJettyBase().resolve("webapps"); + Path webAppDirLib = webapps.resolve(name + ".d").resolve("lib"); + Path webAppJar = distribution.resolveArtifact("org.eclipse.jetty:jetty-test-http2-client-transport-provided-webapp:jar:" + jettyVersion); + Files.copy(webAppJar, Files.createDirectories(webAppDirLib).resolve("webapp.jar")); + Files.writeString(webapps.resolve(name + ".xml"), """ + + + + /test + + + + + """); + + int port = Tester.freePort(); + try (JettyHomeTester.Run run2 = distribution.start("jetty.http.port=" + port)) + { + assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS)); + + startHttpClient(); + URI serverUri = URI.create("http://localhost:" + port + "/test/"); + ContentResponse response = client.newRequest(serverUri) + .timeout(15, TimeUnit.SECONDS) + .send(); + assertEquals(HttpStatus.OK_200, response.getStatus()); + } + } + } }