Issue #5320 - reproduce failure to load httpClient for WebSocketClient in webapp

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2020-09-29 18:47:34 +10:00
parent e3ed05fc1c
commit 00f05cb94e
10 changed files with 316 additions and 8 deletions

View File

@ -35,15 +35,13 @@ public final class HttpClientProvider
Class<?> xmlClazz = Class.forName("org.eclipse.jetty.websocket.client.XmlBasedHttpClientProvider");
Method getMethod = xmlClazz.getMethod("get", WebSocketContainerScope.class);
Object ret = getMethod.invoke(null, scope);
if ((ret != null) && (ret instanceof HttpClient))
{
if (ret instanceof HttpClient)
return (HttpClient)ret;
}
}
}
catch (Throwable ignore)
catch (Throwable t)
{
Log.getLogger(HttpClientProvider.class).ignore(ignore);
Log.getLogger(HttpClientProvider.class).ignore(t);
}
return DefaultHttpClientProvider.newHttpClient(scope);

View File

@ -22,6 +22,7 @@ import java.net.URL;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.xml.XmlConfiguration;
@ -31,13 +32,11 @@ class XmlBasedHttpClientProvider
{
URL resource = Thread.currentThread().getContextClassLoader().getResource("jetty-websocket-httpclient.xml");
if (resource == null)
{
return null;
}
try
{
XmlConfiguration configuration = new XmlConfiguration(resource);
XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(resource));
return (HttpClient)configuration.configure();
}
catch (Throwable t)

View File

@ -102,6 +102,18 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-api</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-felix-webapp</artifactId>

View File

@ -19,9 +19,11 @@
package org.eclipse.jetty.tests.distribution;
import java.io.File;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.HttpClient;
@ -31,12 +33,18 @@ import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.unixsocket.UnixSocketConnector;
import org.eclipse.jetty.unixsocket.client.HttpClientTransportOverUnixSockets;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@ -301,4 +309,95 @@ public class DistributionTests extends AbstractDistributionTest
IO.delete(jettyBase.toFile());
}
}
@ParameterizedTest
@ValueSource(strings = {"", "--jpms"})
@DisabledOnJre({JRE.JAVA_14, JRE.JAVA_15})
public void testWebsocketClientInWebapp(String arg) throws Exception
{
Path jettyBase = Files.createTempDirectory("jetty_base");
String jettyVersion = System.getProperty("jettyVersion");
DistributionTester distribution = DistributionTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.jettyBase(jettyBase)
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();
String[] args1 = {
"--create-startd",
"--approve-all-licenses",
"--add-to-start=resources,server,http,webapp,deploy,jsp,jmx,servlet,servlets,websocket"
};
try (DistributionTester.Run run1 = distribution.start(args1))
{
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
File webApp = distribution.resolveArtifact("org.eclipse.jetty.tests:test-websocket-client-webapp:war:" + jettyVersion);
distribution.installWarFile(webApp, "test");
int port = distribution.freePort();
String[] args2 = {
arg,
"jetty.http.port=" + port,
// "jetty.server.dumpAfterStart=true",
// "jetty.webapp.addSystemClasses+=,org.eclipse.jetty.client.",
// "jetty.webapp.addServerClasses+=,-org.eclipse.jetty.client.",
// "jetty.webapp.addSystemClasses+=,org.eclipse.jetty.util.ssl.",
// "jetty.webapp.addServerClasses+=,-org.eclipse.jetty.util.ssl.",
// "jetty.webapp.addSystemClasses+=,org.eclipse.jetty.util.component.",
// "jetty.webapp.addServerClasses+=,-org.eclipse.jetty.util.component."
};
try (DistributionTester.Run run2 = distribution.start(args2))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
// We should get the correct configuration from the jetty-websocket-httpclient.xml file.
startHttpClient();
URI serverUri = URI.create("ws://localhost:" + port + "/test");
ContentResponse response = client.GET(serverUri);
assertEquals(HttpStatus.OK_200, response.getStatus());
String content = response.getContentAsString();
System.err.println(content);
assertThat(content, containsString("ConnectTimeout: 4999"));
assertThat(content, containsString("WebSocketEcho: success"));
}
}
}
public static class WsListener implements WebSocketListener
{
public BlockingArrayQueue<String> textMessages = new BlockingArrayQueue<>();
public final CountDownLatch closeLatch = new CountDownLatch(1);
public int closeCode;
@Override
public void onWebSocketClose(int statusCode, String reason)
{
this.closeCode = statusCode;
closeLatch.countDown();
}
@Override
public void onWebSocketConnect(Session session)
{
}
@Override
public void onWebSocketError(Throwable cause)
{
}
@Override
public void onWebSocketBinary(byte[] payload, int offset, int len)
{
}
@Override
public void onWebSocketText(String message)
{
textMessages.add(message);
}
}
}

View File

@ -44,5 +44,6 @@
<module>test-cdi-common-webapp</module>
<module>test-weld-cdi-webapp</module>
<module>test-owb-cdi-webapp</module>
<module>test-websocket-client-webapp</module>
</modules>
</project>

View File

@ -0,0 +1,33 @@
<?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">
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-webapps-parent</artifactId>
<version>9.4.32-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-websocket-client-webapp</artifactId>
<packaging>war</packaging>
<name>Test :: Jetty Websocket Simple Webapp with WebSocketClient</name>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,32 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.tests.webapp.websocket;
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value = "/echo")
public class EchoEndpoint
{
@OnMessage
public String echo(String message)
{
return message;
}
}

View File

@ -0,0 +1,104 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.tests.webapp.websocket;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.util.WSURI;
import org.eclipse.jetty.websocket.client.WebSocketClient;
@WebServlet("/")
public class WebSocketClientServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
{
WebSocketClient client = null;
try
{
client = new WebSocketClient();
LifeCycle.start(client);
resp.setContentType("text/html");
resp.getWriter().println("ConnectTimeout: " + client.getHttpClient().getConnectTimeout());
ClientSocket clientSocket = new ClientSocket();
URI wsUri = WSURI.toWebsocket(req.getRequestURL()).resolve("echo");
client.connect(clientSocket, wsUri);
clientSocket.openLatch.await(5, TimeUnit.SECONDS);
clientSocket.session.getRemote().sendString("test message");
String response = clientSocket.textMessages.poll(5, TimeUnit.SECONDS);
if (!"test message".equals(response))
throw new RuntimeException("incorrect response");
clientSocket.session.close();
clientSocket.closeLatch.await(5, TimeUnit.SECONDS);
resp.getWriter().println("WebSocketEcho: success");
}
catch (Exception e)
{
throw new RuntimeException(e);
}
finally
{
LifeCycle.stop(client);
}
}
@WebSocket
public static class ClientSocket
{
public Session session;
public CountDownLatch openLatch = new CountDownLatch(1);
public CountDownLatch closeLatch = new CountDownLatch(1);
public ArrayBlockingQueue<String> textMessages = new ArrayBlockingQueue<>(10);
@OnWebSocketConnect
public void onOpen(Session session)
{
this.session = session;
openLatch.countDown();
}
@OnWebSocketMessage
public void onMessage(String message)
{
textMessages.add(message);
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
closeLatch.countDown();
}
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.client.HttpClient">
<Arg>
<New class="org.eclipse.jetty.util.ssl.SslContextFactory$Client">
<Set name="trustAll" type="java.lang.Boolean">false</Set>
<Call name="addExcludeProtocols">
<Arg>
<Array type="java.lang.String">
<Item>TLS/1.3</Item>
</Array>
</Arg>
</Call>
<Call name="setExcludeCipherSuites"><!-- websocket.org uses WEAK cipher suites -->
<Arg>
<Array type="java.lang.String" />
</Arg>
</Call>
</New>
</Arg>
<Set name="connectTimeout">4999</Set>
</Configure>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
metadata-complete="false"
version="3.1">
</web-app>