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 <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2024-08-23 17:33:51 +03:00 committed by GitHub
parent 6527aa559b
commit bb52d95a62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 188 additions and 4 deletions

View File

@ -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

View File

@ -385,6 +385,10 @@ public class ContextProvider extends ScanningAppProvider
// 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 @@ public class ContextProvider extends ScanningAppProvider
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))
{

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-tests</artifactId>
<version>12.0.13-SNAPSHOT</version>
</parent>
<artifactId>jetty-test-http2-client-transport-provided-webapp</artifactId>
<name>Core :: Tests :: HTTP/2 Client Transport Provided WebApp</name>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client-transport</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -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;
}
}

View File

@ -14,6 +14,7 @@
<modules>
<module>jetty-test-client-transports</module>
<module>jetty-test-jmx</module>
<module>jetty-test-http2-client-transport-provided-webapp</module>
</modules>
<properties>

View File

@ -158,6 +158,10 @@
<artifactId>jetty-gcloud-session-manager</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client-transport</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-server</artifactId>

View File

@ -1892,4 +1892,54 @@ public class DistributionTests extends AbstractJettyHomeTest
}
}
}
@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"), """
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<Set name="contextPath">/test</Set>
<Set name="handler">
<New class="org.eclipse.jetty.test.http2.client.transport.provided.HTTP2ClientTransportProvidedHandler" />
</Set>
</Configure>
""");
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());
}
}
}
}