From 00f05cb94e2dcd86523202bee380776d58a06240 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 29 Sep 2020 18:47:34 +1000 Subject: [PATCH] Issue #5320 - reproduce failure to load httpClient for WebSocketClient in webapp Signed-off-by: Lachlan Roberts --- .../websocket/client/HttpClientProvider.java | 8 +- .../client/XmlBasedHttpClientProvider.java | 5 +- tests/test-distribution/pom.xml | 12 ++ .../tests/distribution/DistributionTests.java | 99 +++++++++++++++++ tests/test-webapps/pom.xml | 1 + .../test-websocket-client-webapp/pom.xml | 33 ++++++ .../tests/webapp/websocket/EchoEndpoint.java | 32 ++++++ .../websocket/WebSocketClientServlet.java | 104 ++++++++++++++++++ .../resources/jetty-websocket-httpclient.xml | 22 ++++ .../src/main/webapp/WEB-INF/web.xml | 8 ++ 10 files changed, 316 insertions(+), 8 deletions(-) create mode 100644 tests/test-webapps/test-websocket-client-webapp/pom.xml create mode 100644 tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/EchoEndpoint.java create mode 100644 tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/WebSocketClientServlet.java create mode 100644 tests/test-webapps/test-websocket-client-webapp/src/main/resources/jetty-websocket-httpclient.xml create mode 100644 tests/test-webapps/test-websocket-client-webapp/src/main/webapp/WEB-INF/web.xml diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/HttpClientProvider.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/HttpClientProvider.java index e2c89dab64df..cc7a5fa9d08b 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/HttpClientProvider.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/HttpClientProvider.java @@ -35,15 +35,13 @@ public static HttpClient get(WebSocketContainerScope scope) 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); diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/XmlBasedHttpClientProvider.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/XmlBasedHttpClientProvider.java index e4a7a09199f5..a90a944efbcf 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/XmlBasedHttpClientProvider.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/XmlBasedHttpClientProvider.java @@ -22,6 +22,7 @@ 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 @@ public static HttpClient get(@SuppressWarnings("unused") WebSocketContainerScope { 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) diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml index 04d73c9fcb93..d247df726295 100644 --- a/tests/test-distribution/pom.xml +++ b/tests/test-distribution/pom.xml @@ -102,6 +102,18 @@ ${project.version} test + + org.eclipse.jetty.websocket + websocket-api + ${project.version} + test + + + org.eclipse.jetty.websocket + websocket-client + ${project.version} + test + org.eclipse.jetty.tests test-felix-webapp diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java index 72bd88c0bd64..2b2fc91e358e 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -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.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 void testLog4j2ModuleWithSimpleWebAppWithJSP() throws Exception 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 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); + } + } } diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml index f7b81a61182e..e57b053ca111 100644 --- a/tests/test-webapps/pom.xml +++ b/tests/test-webapps/pom.xml @@ -44,5 +44,6 @@ test-cdi-common-webapp test-weld-cdi-webapp test-owb-cdi-webapp + test-websocket-client-webapp diff --git a/tests/test-webapps/test-websocket-client-webapp/pom.xml b/tests/test-webapps/test-websocket-client-webapp/pom.xml new file mode 100644 index 000000000000..6bd4a5cb9e32 --- /dev/null +++ b/tests/test-webapps/test-websocket-client-webapp/pom.xml @@ -0,0 +1,33 @@ + + + + org.eclipse.jetty.tests + test-webapps-parent + 9.4.32-SNAPSHOT + + + 4.0.0 + test-websocket-client-webapp + war + + Test :: Jetty Websocket Simple Webapp with WebSocketClient + + + + javax.servlet + javax.servlet-api + provided + + + javax.websocket + javax.websocket-api + provided + + + org.eclipse.jetty.websocket + websocket-client + ${project.version} + provided + + + diff --git a/tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/EchoEndpoint.java b/tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/EchoEndpoint.java new file mode 100644 index 000000000000..2120c5d7cca3 --- /dev/null +++ b/tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/EchoEndpoint.java @@ -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; + } +} diff --git a/tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/WebSocketClientServlet.java b/tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/WebSocketClientServlet.java new file mode 100644 index 000000000000..2b026ac5a80f --- /dev/null +++ b/tests/test-webapps/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/tests/webapp/websocket/WebSocketClientServlet.java @@ -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 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(); + } + } +} diff --git a/tests/test-webapps/test-websocket-client-webapp/src/main/resources/jetty-websocket-httpclient.xml b/tests/test-webapps/test-websocket-client-webapp/src/main/resources/jetty-websocket-httpclient.xml new file mode 100644 index 000000000000..469550c5b5a6 --- /dev/null +++ b/tests/test-webapps/test-websocket-client-webapp/src/main/resources/jetty-websocket-httpclient.xml @@ -0,0 +1,22 @@ + + + + + + false + + + + TLS/1.3 + + + + + + + + + + + 4999 + diff --git a/tests/test-webapps/test-websocket-client-webapp/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-websocket-client-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..99907fad52a3 --- /dev/null +++ b/tests/test-webapps/test-websocket-client-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,8 @@ + + +