Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #5320 - using jetty-websocket-httpclient.xml within webapp #5374

Merged
merged 13 commits into from Nov 2, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions jetty-websocket/websocket-client/pom.xml
Expand Up @@ -25,6 +25,12 @@
<artifactId>jetty-xml</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
Expand Down
Expand Up @@ -18,33 +18,16 @@

package org.eclipse.jetty.websocket.client;

import java.lang.reflect.Method;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;

public final class HttpClientProvider
{
public static HttpClient get(WebSocketContainerScope scope)
{
try
{
if (Class.forName("org.eclipse.jetty.xml.XmlConfiguration") != null)
{
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))
{
return (HttpClient)ret;
}
}
}
catch (Throwable ignore)
{
Log.getLogger(HttpClientProvider.class).ignore(ignore);
}
HttpClient httpClient = XmlBasedHttpClientProvider.get(scope);
if (httpClient != null)
return httpClient;

return DefaultHttpClientProvider.newHttpClient(scope);
}
Expand Down
Expand Up @@ -22,27 +22,59 @@

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppClassLoader;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.xml.XmlConfiguration;

class XmlBasedHttpClientProvider
{
public static final Logger LOG = Log.getLogger(XmlBasedHttpClientProvider.class);

public static HttpClient get(@SuppressWarnings("unused") WebSocketContainerScope scope)
{
URL resource = Thread.currentThread().getContextClassLoader().getResource("jetty-websocket-httpclient.xml");
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (contextClassLoader == null)
return null;

URL resource = contextClassLoader.getResource("jetty-websocket-httpclient.xml");
if (resource == null)
{
return null;

// Try to load a HttpClient from a jetty-websocket-httpclient.xml configuration file.
// If WebAppClassLoader run with server class access, otherwise run normally.
try
{
try
{
return WebAppClassLoader.runWithServerClassAccess(() -> newHttpClient(resource));
}
catch (NoClassDefFoundError | ClassNotFoundException e)
{
if (LOG.isDebugEnabled())
LOG.debug("Could not use WebAppClassLoader to run with Server class access", e);
return newHttpClient(resource);
}
}
catch (Throwable t)
{
LOG.warn("Failure to load HttpClient from XML", t);
}
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved

return null;
}

private static HttpClient newHttpClient(URL resource)
{
try
{
XmlConfiguration configuration = new XmlConfiguration(resource);
XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(resource));
return (HttpClient)configuration.configure();
}
catch (Throwable t)
{
Log.getLogger(XmlBasedHttpClientProvider.class).warn("Unable to load: " + resource, t);
LOG.warn("Unable to load: {}", resource, t);
}

return null;
Expand Down
12 changes: 12 additions & 0 deletions tests/test-distribution/pom.xml
Expand Up @@ -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>
Expand Down
Expand Up @@ -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;
Expand All @@ -31,12 +33,17 @@
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.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;
Expand Down Expand Up @@ -301,4 +308,91 @@ public void testLog4j2ModuleWithSimpleWebAppWithJSP() throws Exception
IO.delete(jettyBase.toFile());
}
}

@Test
public void testWebsocketClientInWebapp() 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 = {
"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");
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved
ContentResponse response = client.GET(serverUri);
assertEquals(HttpStatus.OK_200, response.getStatus());
String content = response.getContentAsString();
// assertThat(content, containsString("ConnectTimeout: 4999")); // TODO: how to test this?
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);
}
}
}
1 change: 1 addition & 0 deletions tests/test-webapps/pom.xml
Expand Up @@ -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>
33 changes: 33 additions & 0 deletions tests/test-webapps/test-websocket-client-webapp/pom.xml
@@ -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>
@@ -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;
}
}