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 1287200826d3..480507a42fe4 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://eclipse.dev/jetty/documentation/
[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 a27529f92c30..6e219b216e3a 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());
+ }
+ }
+ }
}