diff --git a/jetty-websocket/websocket-client/pom.xml b/jetty-websocket/websocket-client/pom.xml index c32eb1ebc1c..9882a210ffb 100644 --- a/jetty-websocket/websocket-client/pom.xml +++ b/jetty-websocket/websocket-client/pom.xml @@ -20,6 +20,12 @@ jetty-util ${project.version} + + org.eclipse.jetty + jetty-xml + ${project.version} + true + org.eclipse.jetty jetty-io diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/DefaultHttpClientProvider.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/DefaultHttpClientProvider.java new file mode 100644 index 00000000000..91747c8be96 --- /dev/null +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/DefaultHttpClientProvider.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.websocket.client; + +import java.util.concurrent.Executor; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; + +class DefaultHttpClientProvider +{ + public static HttpClient newHttpClient(WebSocketContainerScope scope) + { + SslContextFactory sslContextFactory = null; + Executor executor = null; + + if (scope != null) + { + sslContextFactory = scope.getSslContextFactory(); + executor = scope.getExecutor(); + } + + if (sslContextFactory == null) + { + sslContextFactory = new SslContextFactory(); + } + + HttpClient client = new HttpClient(sslContextFactory); + if (executor == null) + { + QueuedThreadPool threadPool = new QueuedThreadPool(); + String name = "WebSocketClient@" + client.hashCode(); + threadPool.setName(name); + threadPool.setDaemon(true); + executor = threadPool; + } + client.setExecutor(executor); + return client; + } +} 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 new file mode 100644 index 00000000000..ede9dcb0eb5 --- /dev/null +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/HttpClientProvider.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.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).warn(ignore); + } + + return DefaultHttpClientProvider.newHttpClient(scope); + } +} diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java index ab6494776c9..fc68ea00146 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java @@ -39,7 +39,6 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.ShutdownThread; import org.eclipse.jetty.websocket.api.Session; @@ -84,7 +83,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont public WebSocketClient() { // Create synthetic HttpClient - this(new HttpClient()); + this(HttpClientProvider.get(null)); addBean(this.httpClient); } @@ -262,23 +261,8 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont } this.containerScope = clientScope; - SslContextFactory sslContextFactory = scope.getSslContextFactory(); - if(sslContextFactory == null) - { - sslContextFactory = new SslContextFactory(); - } - this.httpClient = new HttpClient(sslContextFactory); - Executor executor = scope.getExecutor(); - if (executor == null) - { - QueuedThreadPool threadPool = new QueuedThreadPool(); - String name = "WebSocketClient@" + hashCode(); - threadPool.setName(name); - threadPool.setDaemon(true); - executor = threadPool; - } - - this.httpClient.setExecutor(executor); + + this.httpClient = HttpClientProvider.get(scope); addBean(this.httpClient); this.extensionRegistry = new WebSocketExtensionFactory(containerScope); 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 new file mode 100644 index 00000000000..2608c92d328 --- /dev/null +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/XmlBasedHttpClientProvider.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.websocket.client; + +import java.io.InputStream; +import java.net.URL; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; +import org.eclipse.jetty.xml.XmlConfiguration; + +class XmlBasedHttpClientProvider +{ + public static HttpClient get(@SuppressWarnings("unused") WebSocketContainerScope scope) + { + URL resource = Thread.currentThread().getContextClassLoader().getResource("jetty-websocket-httpclient.xml"); + if (resource == null) + { + return null; + } + + try (InputStream in = resource.openStream()) + { + XmlConfiguration configuration = new XmlConfiguration(in); + return (HttpClient) configuration.configure(); + } + catch (Throwable t) + { + Log.getLogger(XmlBasedHttpClientProvider.class).warn("Unable to load: " + resource, t); + } + + return null; + } +} diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/HttpClientInitTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/HttpClientInitTest.java new file mode 100644 index 00000000000..d7763a8524f --- /dev/null +++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/HttpClientInitTest.java @@ -0,0 +1,133 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.websocket.client; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ThreadClassLoaderScope; +import org.junit.Test; + +public class HttpClientInitTest +{ + @Test + public void testDefaultInit() throws Exception + { + WebSocketClient client = new WebSocketClient(); + try + { + client.start(); + HttpClient httpClient = client.getHttpClient(); + assertThat("HttpClient exists", httpClient, notNullValue()); + assertThat("HttpClient is started", httpClient.isStarted(), is(true)); + Executor executor = httpClient.getExecutor(); + assertThat("Executor exists", executor, notNullValue()); + assertThat("Executor instanceof", executor, instanceOf(QueuedThreadPool.class)); + QueuedThreadPool threadPool = (QueuedThreadPool) executor; + assertThat("QueuedThreadPool.name", threadPool.getName(), startsWith("WebSocketClient@")); + } + finally + { + client.stop(); + } + } + + @Test + public void testManualInit() throws Exception + { + HttpClient http = new HttpClient(); + { + QueuedThreadPool threadPool = new QueuedThreadPool(); + threadPool.setName("ManualWSClient@" + http.hashCode()); + http.setExecutor(threadPool); + http.setConnectTimeout(7777); + } + + WebSocketClient client = new WebSocketClient(http); + client.addBean(http); + try + { + client.start(); + HttpClient httpClient = client.getHttpClient(); + assertThat("HttpClient exists", httpClient, notNullValue()); + assertThat("HttpClient is started", httpClient.isStarted(), is(true)); + assertThat("HttpClient.connectTimeout", httpClient.getConnectTimeout(), is(7777L)); + Executor executor = httpClient.getExecutor(); + assertThat("Executor exists", executor, notNullValue()); + assertThat("Executor instanceof", executor, instanceOf(QueuedThreadPool.class)); + QueuedThreadPool threadPool = (QueuedThreadPool) executor; + assertThat("QueuedThreadPool.name", threadPool.getName(), startsWith("ManualWSClient@")); + } + finally + { + client.stop(); + } + } + + @Test + public void testXmlResourceInit() throws Exception + { + ClassLoader parent = Thread.currentThread().getContextClassLoader(); + URL urls[] = new URL[]{ + MavenTestingUtils.getTestResourceDir("httpclient/simple").toURI().toURL() + }; + URLClassLoader classLoader = new URLClassLoader(urls, parent); + + try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(classLoader)) + { + WebSocketClient client = new WebSocketClient(); + try + { + client.start(); + HttpClient httpClient = client.getHttpClient(); + assertThat("HttpClient exists", httpClient, notNullValue()); + assertThat("HttpClient is started", httpClient.isStarted(), is(true)); + assertThat("HttpClient.connectTimeout", httpClient.getConnectTimeout(), is(5555L)); + + SslContextFactory sslContextFactory = httpClient.getSslContextFactory(); + List actualExcludedProtocols = Arrays.asList(sslContextFactory.getExcludeProtocols()); + assertThat("HttpClient.sslContextFactory.excludedProtocols", actualExcludedProtocols, hasItem("TLS/0.1")); + + Executor executor = httpClient.getExecutor(); + assertThat("Executor exists", executor, notNullValue()); + assertThat("Executor instanceof", executor, instanceOf(QueuedThreadPool.class)); + QueuedThreadPool threadPool = (QueuedThreadPool) executor; + assertThat("QueuedThreadPool.name", threadPool.getName(), startsWith("XmlBasedClient@")); + } + finally + { + client.stop(); + } + } + } +} diff --git a/jetty-websocket/websocket-client/src/test/resources/httpclient/simple/jetty-websocket-httpclient.xml b/jetty-websocket/websocket-client/src/test/resources/httpclient/simple/jetty-websocket-httpclient.xml new file mode 100644 index 00000000000..86ed90f948a --- /dev/null +++ b/jetty-websocket/websocket-client/src/test/resources/httpclient/simple/jetty-websocket-httpclient.xml @@ -0,0 +1,23 @@ + + + + + + + false + + + + TLS/0.1 + + + + + + 5555 + + + XmlBasedClient@ + + + \ No newline at end of file