Adding javax.websocket secure client example

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2019-08-22 16:45:04 -05:00
parent d0ec6e7d07
commit 5bcbe0f9d9
4 changed files with 277 additions and 0 deletions

View File

@ -0,0 +1,90 @@
//
// ========================================================================
// Copyright (c) 1995-2019 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 examples;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
/**
* Basic Echo Client Endpoint
*/
public class EchoEndpoint extends Endpoint implements MessageHandler.Whole<String>
{
private final CountDownLatch closeLatch = new CountDownLatch(1);
private Session session;
public boolean awaitClose(int duration, TimeUnit unit) throws InterruptedException
{
return this.closeLatch.await(duration, unit);
}
@Override
public void onClose(Session session, CloseReason closeReason)
{
System.out.printf("Connection closed: Session.id=%s - %s%n", session.getId(), closeReason);
this.session = null;
this.closeLatch.countDown(); // trigger latch
}
@Override
public void onOpen(Session session, EndpointConfig config)
{
System.out.printf("Got open: Session.id=%s%n", session.getId());
this.session = session;
this.session.addMessageHandler(this);
try
{
session.getBasicRemote().sendText("Hello");
session.getBasicRemote().sendText("Thanks for the conversation.");
}
catch (Throwable t)
{
t.printStackTrace();
}
}
@Override
public void onMessage(String msg)
{
System.out.printf("Got msg: \"%s\"%n", msg);
if (msg.contains("Thanks"))
{
try
{
session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "I'm done"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
@Override
public void onError(Session session, Throwable cause)
{
cause.printStackTrace();
}
}

View File

@ -0,0 +1,44 @@
//
// ========================================================================
// Copyright (c) 1995-2019 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 examples;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.websocket.ClientEndpointConfig;
/**
* Provide a means to set the `Origin` header for outgoing WebSocket upgrade requests
*/
public class OriginServerConfigurator extends ClientEndpointConfig.Configurator
{
private final String originServer;
public OriginServerConfigurator(String originServer)
{
this.originServer = originServer;
}
@Override
public void beforeRequest(Map<String, List<String>> headers)
{
headers.put("Origin", Collections.singletonList(originServer));
super.beforeRequest(headers);
}
}

View File

@ -0,0 +1,121 @@
//
// ========================================================================
// Copyright (c) 1995-2019 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 examples;
import java.io.FileNotFoundException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.TimeUnit;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.ThreadClassLoaderScope;
public class SecureWebSocketContainerExample
{
public static void main(String[] args)
{
String destUri = "wss://echo.websocket.org";
if (args.length > 0)
{
destUri = args[0];
}
WebSocketContainer client = null;
try
{
client = getConfiguredWebSocketContainer();
URI echoUri = new URI(destUri);
ClientEndpointConfig clientEndpointConfig = ClientEndpointConfig.Builder.create()
.configurator(new OriginServerConfigurator("https://websocket.org"))
.build();
EchoEndpoint echoEndpoint = new EchoEndpoint();
client.connectToServer(echoEndpoint, clientEndpointConfig, echoUri);
System.out.printf("Connecting to : %s%n", echoUri);
// wait for closed socket connection.
echoEndpoint.awaitClose(5, TimeUnit.SECONDS);
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
/* Since javax.websocket clients have no defined LifeCycle we
* want to either close/stop the client, or exit the JVM
* via a System.exit(), otherwise the threads this client keeps
* open will prevent the JVM from terminating naturally.
*/
LifeCycle.stop(client);
}
}
/**
* Since javax.websocket does not have an API for configuring SSL, each implementation
* of javax.websocket has to come up with their own SSL configuration mechanism.
* <p>
* When the call to {@link javax.websocket.ContainerProvider}.{@link ContainerProvider#getWebSocketContainer()}
* occurs, that needs to have a started and available WebSocket Client.
* Jetty's {@code WebSocketClient} must have a Jetty {@code HttpClient} started as well.
* If you want SSL, then that configuration has to be passed into the Jetty {@code HttpClient} at initialization.
* </p>
* <p>
* How Jetty makes this available, is via the {@code jetty-websocket-httpclient.xml} classloader resource
* along with the jetty-xml artifact.
* </p>
* <p>
* This method will look for the file in the classloader resources, and then
* sets up a {@link URLClassLoader} to make that {@code jetty-websocket-httpclient.xml} available
* for this specific example.
* If we had put the `jetty-websocket-httpclient.xml` in the root of a JAR file loaded by this
* project then you can skip all of the classloader trickery this method performs.
* </p>
*
* @return the client WebSocketContainer
* @see <a href="https://github.com/eclipse-ee4j/websocket-api/issues/210">javax.websocket issue #210</a>
*/
public static WebSocketContainer getConfiguredWebSocketContainer() throws Exception
{
URL jettyHttpClientConfigUrl = Thread.currentThread().getContextClassLoader()
.getResource("examples/jetty-websocket-httpclient.xml");
if (jettyHttpClientConfigUrl == null)
{
throw new FileNotFoundException("Unable to find Jetty HttpClient configuration XML");
}
URI jettyConfigDirUri = jettyHttpClientConfigUrl.toURI().resolve("./");
ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
URL[] urls = new URL[]{
jettyConfigDirUri.toURL()
};
URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader);
try (ThreadClassLoaderScope ignore = new ThreadClassLoaderScope(classLoader))
{
return ContainerProvider.getWebSocketContainer();
}
}
}

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">5000</Set>
</Configure>