Cleaning up all of the old uncompilable websocket source
This commit is contained in:
parent
7d4da60d05
commit
d70174f159
|
@ -1,600 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2011 Intalio, Inc.
|
|
||||||
* ======================================================================
|
|
||||||
* 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.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.ProtocolException;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.channels.ByteChannel;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocketConnection;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocketConnectionRFC6455;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket.Connection;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket.FrameConnection;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket.OnFrame;
|
|
||||||
import org.eclipse.jetty.websocket.masks.Masker;
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* <p>{@link OldWebSocketClient} allows to create multiple connections to multiple destinations
|
|
||||||
* that can speak the websocket protocol.</p>
|
|
||||||
* <p>When creating websocket connections, {@link OldWebSocketClient} accepts a {@link WebSocket}
|
|
||||||
* object (to receive events from the server), and returns a {@link WebSocket.Connection} to
|
|
||||||
* send data to the server.</p>
|
|
||||||
* <p>Example usage is as follows:</p>
|
|
||||||
* <pre>
|
|
||||||
* WebSocketClientFactory factory = new WebSocketClientFactory();
|
|
||||||
* factory.start();
|
|
||||||
*
|
|
||||||
* WebSocketClient client = factory.newWebSocketClient();
|
|
||||||
* // Configure the client
|
|
||||||
*
|
|
||||||
* WebSocket.Connection connection = client.open(new URI("ws://127.0.0.1:8080/"), new WebSocket.OnTextMessage()
|
|
||||||
* {
|
|
||||||
* public void onOpen(Connection connection)
|
|
||||||
* {
|
|
||||||
* // open notification
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* public void onClose(int closeCode, String message)
|
|
||||||
* {
|
|
||||||
* // close notification
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* public void onMessage(String data)
|
|
||||||
* {
|
|
||||||
* // handle incoming message
|
|
||||||
* }
|
|
||||||
* }).get(5, TimeUnit.SECONDS);
|
|
||||||
*
|
|
||||||
* connection.sendMessage("Hello World");
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
public class OldWebSocketClient
|
|
||||||
{
|
|
||||||
private final static Logger __log = org.eclipse.jetty.util.log.Log.getLogger(OldWebSocketClient.class.getName());
|
|
||||||
|
|
||||||
private final OldWebSocketClientFactory _factory;
|
|
||||||
private final Map<String,String> _cookies=new ConcurrentHashMap<String, String>();
|
|
||||||
private final List<String> _extensions=new CopyOnWriteArrayList<String>();
|
|
||||||
private String _origin;
|
|
||||||
private String _protocol;
|
|
||||||
private int _maxIdleTime=-1;
|
|
||||||
private int _maxTextMessageSize=16*1024;
|
|
||||||
private int _maxBinaryMessageSize=-1;
|
|
||||||
private Masker _maskGen;
|
|
||||||
private SocketAddress _bindAddress;
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* <p>Creates a WebSocketClient from a private WebSocketClientFactory.</p>
|
|
||||||
* <p>This can be wasteful of resources if many clients are created.</p>
|
|
||||||
*
|
|
||||||
* @deprecated Use {@link OldWebSocketClientFactory#newWebSocketClient()}
|
|
||||||
* @throws Exception if the private WebSocketClientFactory fails to start
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public OldWebSocketClient() throws Exception
|
|
||||||
{
|
|
||||||
_factory=new OldWebSocketClientFactory();
|
|
||||||
_factory.start();
|
|
||||||
_maskGen=_factory.getMaskGen();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* <p>Creates a WebSocketClient with shared WebSocketClientFactory.</p>
|
|
||||||
*
|
|
||||||
* @param factory the shared {@link OldWebSocketClientFactory}
|
|
||||||
*/
|
|
||||||
public OldWebSocketClient(OldWebSocketClientFactory factory)
|
|
||||||
{
|
|
||||||
_factory=factory;
|
|
||||||
_maskGen=_factory.getMaskGen();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The WebSocketClientFactory this client was created with.
|
|
||||||
*/
|
|
||||||
public OldWebSocketClientFactory getFactory()
|
|
||||||
{
|
|
||||||
return _factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return the address to bind the socket channel to
|
|
||||||
* @see #setBindAddress(SocketAddress)
|
|
||||||
*/
|
|
||||||
public SocketAddress getBindAddress()
|
|
||||||
{
|
|
||||||
return _bindAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param bindAddress the address to bind the socket channel to
|
|
||||||
* @see #getBindAddress()
|
|
||||||
*/
|
|
||||||
public void setBindAddress(SocketAddress bindAddress)
|
|
||||||
{
|
|
||||||
this._bindAddress = bindAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The maxIdleTime in ms for connections opened by this client,
|
|
||||||
* or -1 if the default from {@link OldWebSocketClientFactory#getSelectorManager()} is used.
|
|
||||||
* @see #setMaxIdleTime(int)
|
|
||||||
*/
|
|
||||||
public int getMaxIdleTime()
|
|
||||||
{
|
|
||||||
return _maxIdleTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param maxIdleTime The max idle time in ms for connections opened by this client
|
|
||||||
* @see #getMaxIdleTime()
|
|
||||||
*/
|
|
||||||
public void setMaxIdleTime(int maxIdleTime)
|
|
||||||
{
|
|
||||||
_maxIdleTime=maxIdleTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The subprotocol string for connections opened by this client.
|
|
||||||
* @see #setProtocol(String)
|
|
||||||
*/
|
|
||||||
public String getProtocol()
|
|
||||||
{
|
|
||||||
return _protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param protocol The subprotocol string for connections opened by this client.
|
|
||||||
* @see #getProtocol()
|
|
||||||
*/
|
|
||||||
public void setProtocol(String protocol)
|
|
||||||
{
|
|
||||||
_protocol = protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The origin URI of the client
|
|
||||||
* @see #setOrigin(String)
|
|
||||||
*/
|
|
||||||
public String getOrigin()
|
|
||||||
{
|
|
||||||
return _origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param origin The origin URI of the client (eg "http://example.com")
|
|
||||||
* @see #getOrigin()
|
|
||||||
*/
|
|
||||||
public void setOrigin(String origin)
|
|
||||||
{
|
|
||||||
_origin = origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* <p>Returns the map of the cookies that are sent during the initial HTTP handshake
|
|
||||||
* that upgrades to the websocket protocol.</p>
|
|
||||||
* @return The read-write cookie map
|
|
||||||
*/
|
|
||||||
public Map<String,String> getCookies()
|
|
||||||
{
|
|
||||||
return _cookies;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The list of websocket protocol extensions
|
|
||||||
*/
|
|
||||||
public List<String> getExtensions()
|
|
||||||
{
|
|
||||||
return _extensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return the mask generator to use, or null if not mask generator should be used
|
|
||||||
* @see #setMaskGen(Masker)
|
|
||||||
*/
|
|
||||||
public Masker getMaskGen()
|
|
||||||
{
|
|
||||||
return _maskGen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param maskGen the mask generator to use, or null if not mask generator should be used
|
|
||||||
* @see #getMaskGen()
|
|
||||||
*/
|
|
||||||
public void setMaskGen(Masker maskGen)
|
|
||||||
{
|
|
||||||
_maskGen = maskGen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The initial maximum text message size (in characters) for a connection
|
|
||||||
*/
|
|
||||||
public int getMaxTextMessageSize()
|
|
||||||
{
|
|
||||||
return _maxTextMessageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Set the initial maximum text message size for a connection. This can be changed by
|
|
||||||
* the application calling {@link WebSocket.Connection#setMaxTextMessageSize(int)}.
|
|
||||||
* @param maxTextMessageSize The default maximum text message size (in characters) for a connection
|
|
||||||
*/
|
|
||||||
public void setMaxTextMessageSize(int maxTextMessageSize)
|
|
||||||
{
|
|
||||||
_maxTextMessageSize = maxTextMessageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The initial maximum binary message size (in bytes) for a connection
|
|
||||||
*/
|
|
||||||
public int getMaxBinaryMessageSize()
|
|
||||||
{
|
|
||||||
return _maxBinaryMessageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Set the initial maximum binary message size for a connection. This can be changed by
|
|
||||||
* the application calling {@link WebSocket.Connection#setMaxBinaryMessageSize(int)}.
|
|
||||||
* @param maxBinaryMessageSize The default maximum binary message size (in bytes) for a connection
|
|
||||||
*/
|
|
||||||
public void setMaxBinaryMessageSize(int maxBinaryMessageSize)
|
|
||||||
{
|
|
||||||
_maxBinaryMessageSize = maxBinaryMessageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* <p>Opens a websocket connection to the URI and blocks until the connection is accepted or there is an error.</p>
|
|
||||||
*
|
|
||||||
* @param uri The URI to connect to.
|
|
||||||
* @param websocket The {@link WebSocket} instance to handle incoming events.
|
|
||||||
* @param maxConnectTime The interval to wait for a successful connection
|
|
||||||
* @param units the units of the maxConnectTime
|
|
||||||
* @return A {@link WebSocket.Connection}
|
|
||||||
* @throws IOException if the connection fails
|
|
||||||
* @throws InterruptedException if the thread is interrupted
|
|
||||||
* @throws TimeoutException if the timeout elapses before the connection is completed
|
|
||||||
* @see #open(URI, WebSocket)
|
|
||||||
*/
|
|
||||||
public WebSocket.Connection open(URI uri, WebSocket websocket,long maxConnectTime,TimeUnit units) throws IOException, InterruptedException, TimeoutException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return open(uri,websocket).get(maxConnectTime,units);
|
|
||||||
}
|
|
||||||
catch (ExecutionException e)
|
|
||||||
{
|
|
||||||
Throwable cause = e.getCause();
|
|
||||||
if (cause instanceof IOException)
|
|
||||||
throw (IOException)cause;
|
|
||||||
if (cause instanceof Error)
|
|
||||||
throw (Error)cause;
|
|
||||||
if (cause instanceof RuntimeException)
|
|
||||||
throw (RuntimeException)cause;
|
|
||||||
throw new RuntimeException(cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* <p>Asynchronously opens a websocket connection and returns a {@link Future} to obtain the connection.</p>
|
|
||||||
* <p>The caller must call {@link Future#get(long, TimeUnit)} if they wish to impose a connect timeout on the open.</p>
|
|
||||||
*
|
|
||||||
* @param uri The URI to connect to.
|
|
||||||
* @param websocket The {@link WebSocket} instance to handle incoming events.
|
|
||||||
* @return A {@link Future} to the {@link WebSocket.Connection}
|
|
||||||
* @throws IOException if the connection fails
|
|
||||||
* @see #open(URI, WebSocket, long, TimeUnit)
|
|
||||||
*/
|
|
||||||
public Future<WebSocket.Connection> open(URI uri, WebSocket websocket) throws IOException
|
|
||||||
{
|
|
||||||
if (!_factory.isStarted())
|
|
||||||
throw new IllegalStateException("Factory !started");
|
|
||||||
|
|
||||||
InetSocketAddress address = toSocketAddress(uri);
|
|
||||||
|
|
||||||
SocketChannel channel = SocketChannel.open();
|
|
||||||
if (_bindAddress != null)
|
|
||||||
channel.socket().bind(_bindAddress);
|
|
||||||
channel.socket().setTcpNoDelay(true);
|
|
||||||
|
|
||||||
WebSocketFuture holder = new WebSocketFuture(websocket, uri, this, channel);
|
|
||||||
|
|
||||||
channel.configureBlocking(false);
|
|
||||||
channel.connect(address);
|
|
||||||
_factory.getSelectorManager().register(channel, holder);
|
|
||||||
|
|
||||||
return holder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InetSocketAddress toSocketAddress(URI uri)
|
|
||||||
{
|
|
||||||
String scheme = uri.getScheme();
|
|
||||||
if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme)))
|
|
||||||
throw new IllegalArgumentException("Bad WebSocket scheme: " + scheme);
|
|
||||||
int port = uri.getPort();
|
|
||||||
if (port == 0)
|
|
||||||
throw new IllegalArgumentException("Bad WebSocket port: " + port);
|
|
||||||
if (port < 0)
|
|
||||||
port = "ws".equals(scheme) ? 80 : 443;
|
|
||||||
|
|
||||||
return new InetSocketAddress(uri.getHost(), port);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/** The Future Websocket Connection.
|
|
||||||
*/
|
|
||||||
static class WebSocketFuture implements Future<WebSocket.Connection>
|
|
||||||
{
|
|
||||||
final WebSocket _websocket;
|
|
||||||
final URI _uri;
|
|
||||||
final OldWebSocketClient _client;
|
|
||||||
final CountDownLatch _done = new CountDownLatch(1);
|
|
||||||
ByteChannel _channel;
|
|
||||||
WebSocketConnection _connection;
|
|
||||||
Throwable _exception;
|
|
||||||
|
|
||||||
private WebSocketFuture(WebSocket websocket, URI uri, OldWebSocketClient client, ByteChannel channel)
|
|
||||||
{
|
|
||||||
_websocket=websocket;
|
|
||||||
_uri=uri;
|
|
||||||
_client=client;
|
|
||||||
_channel=channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onConnection(WebSocketConnection connection)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_client.getFactory().addConnection(connection);
|
|
||||||
|
|
||||||
connection.getConnection().setMaxTextMessageSize(_client.getMaxTextMessageSize());
|
|
||||||
connection.getConnection().setMaxBinaryMessageSize(_client.getMaxBinaryMessageSize());
|
|
||||||
|
|
||||||
WebSocketConnection con;
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
if (_channel!=null)
|
|
||||||
_connection=connection;
|
|
||||||
con=_connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (con!=null)
|
|
||||||
{
|
|
||||||
if (_websocket instanceof WebSocket.OnFrame)
|
|
||||||
((WebSocket.OnFrame)_websocket).onHandshake((WebSocket.FrameConnection)con.getConnection());
|
|
||||||
|
|
||||||
_websocket.onOpen(con.getConnection());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_done.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handshakeFailed(Throwable ex)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ByteChannel channel=null;
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
if (_channel!=null)
|
|
||||||
{
|
|
||||||
channel=_channel;
|
|
||||||
_channel=null;
|
|
||||||
_exception=ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel!=null)
|
|
||||||
{
|
|
||||||
if (ex instanceof ProtocolException)
|
|
||||||
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_PROTOCOL,ex.getMessage());
|
|
||||||
else
|
|
||||||
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,ex.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_done.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String,String> getCookies()
|
|
||||||
{
|
|
||||||
return _client.getCookies();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProtocol()
|
|
||||||
{
|
|
||||||
return _client.getProtocol();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocket getWebSocket()
|
|
||||||
{
|
|
||||||
return _websocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public URI getURI()
|
|
||||||
{
|
|
||||||
return _uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxIdleTime()
|
|
||||||
{
|
|
||||||
return _client.getMaxIdleTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOrigin()
|
|
||||||
{
|
|
||||||
return _client.getOrigin();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Masker getMaskGen()
|
|
||||||
{
|
|
||||||
return _client.getMaskGen();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "[" + _uri + ","+_websocket+"]@"+hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean cancel(boolean mayInterruptIfRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ByteChannel channel=null;
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
if (_connection==null && _exception==null && _channel!=null)
|
|
||||||
{
|
|
||||||
channel=_channel;
|
|
||||||
_channel=null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel!=null)
|
|
||||||
{
|
|
||||||
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,"cancelled");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_done.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCancelled()
|
|
||||||
{
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
return _channel==null && _connection==null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDone()
|
|
||||||
{
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
return _connection!=null && _exception==null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public org.eclipse.jetty.websocket.WebSocket.Connection get() throws InterruptedException, ExecutionException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return get(Long.MAX_VALUE,TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
catch(TimeoutException e)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("The universe has ended",e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public org.eclipse.jetty.websocket.WebSocket.Connection get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
|
|
||||||
TimeoutException
|
|
||||||
{
|
|
||||||
_done.await(timeout,unit);
|
|
||||||
ByteChannel channel=null;
|
|
||||||
org.eclipse.jetty.websocket.WebSocket.Connection connection=null;
|
|
||||||
Throwable exception;
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
exception=_exception;
|
|
||||||
if (_connection==null)
|
|
||||||
{
|
|
||||||
exception=_exception;
|
|
||||||
channel=_channel;
|
|
||||||
_channel=null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
connection=_connection.getConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel!=null)
|
|
||||||
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,"timeout");
|
|
||||||
if (exception!=null)
|
|
||||||
throw new ExecutionException(exception);
|
|
||||||
if (connection!=null)
|
|
||||||
return connection;
|
|
||||||
throw new TimeoutException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeChannel(ByteChannel channel,int code, String message)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_websocket.onClose(code,message);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
__log.warn(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
channel.close();
|
|
||||||
}
|
|
||||||
catch(IOException e)
|
|
||||||
{
|
|
||||||
__log.debug(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,426 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2011 Intalio, Inc.
|
|
||||||
* ======================================================================
|
|
||||||
* 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.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ProtocolException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SelectionKey;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.io.AsyncConnection;
|
|
||||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
|
||||||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
|
||||||
import org.eclipse.jetty.io.SelectorManager;
|
|
||||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
|
||||||
import org.eclipse.jetty.util.B64Code;
|
|
||||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
|
||||||
import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
|
||||||
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.ThreadPool;
|
|
||||||
import org.eclipse.jetty.websocket.annotations.WebSocket;
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketConnection;
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
|
||||||
import org.eclipse.jetty.websocket.extensions.Extension;
|
|
||||||
import org.eclipse.jetty.websocket.masks.Masker;
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* <p>WebSocketClientFactory contains the common components needed by multiple {@link OldWebSocketClient} instances
|
|
||||||
* (for example, a {@link ThreadPool}, a {@link SelectorManager NIO selector}, etc).</p>
|
|
||||||
* <p>WebSocketClients with different configurations should share the same factory to avoid to waste resources.</p>
|
|
||||||
* <p>If a ThreadPool or MaskGen is passed in the constructor, then it is not added with {@link AggregateLifeCycle#addBean(Object)},
|
|
||||||
* so it's lifecycle must be controlled externally.
|
|
||||||
*
|
|
||||||
* @see OldWebSocketClient
|
|
||||||
*/
|
|
||||||
public class OldWebSocketClientFactory extends AggregateLifeCycle
|
|
||||||
{
|
|
||||||
private final static Logger LOG = Log.getLogger(OldWebSocketClientFactory.class);
|
|
||||||
private final Queue<WebSocketConnection> connections = new ConcurrentLinkedQueue<WebSocketConnection>();
|
|
||||||
private WebSocketPolicy policy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Creates a WebSocketClientFactory with the default configuration.</p>
|
|
||||||
*/
|
|
||||||
public OldWebSocketClientFactory()
|
|
||||||
{
|
|
||||||
policy = WebSocketPolicy.newClientPolicy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocketPolicy getPolicy()
|
|
||||||
{
|
|
||||||
return policy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doStop() throws Exception
|
|
||||||
{
|
|
||||||
closeConnections();
|
|
||||||
super.doStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Creates and returns a new instance of a {@link OldWebSocketClient}, configured with this
|
|
||||||
* WebSocketClientFactory instance.</p>
|
|
||||||
*
|
|
||||||
* @return a new {@link OldWebSocketClient} instance
|
|
||||||
*/
|
|
||||||
public OldWebSocketClient newWebSocketClient()
|
|
||||||
{
|
|
||||||
return new OldWebSocketClient(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean addConnection(WebSocketConnection connection)
|
|
||||||
{
|
|
||||||
return isRunning() && connections.add(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean removeConnection(WebSocketConnection connection)
|
|
||||||
{
|
|
||||||
return connections.remove(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void closeConnections()
|
|
||||||
{
|
|
||||||
for (WebSocketConnection connection : connections)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOG.info(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebSocket Client Selector Manager
|
|
||||||
*/
|
|
||||||
class WebSocketClientSelector extends SelectorManager
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean dispatch(Runnable task)
|
|
||||||
{
|
|
||||||
return _threadPool.dispatch(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, final SelectionKey key) throws IOException
|
|
||||||
{
|
|
||||||
OldWebSocketClient.WebSocketFuture holder = (OldWebSocketClient.WebSocketFuture)key.attachment();
|
|
||||||
int maxIdleTime = holder.getMaxIdleTime();
|
|
||||||
if (maxIdleTime < 0)
|
|
||||||
maxIdleTime = (int)getMaxIdleTime();
|
|
||||||
SelectChannelEndPoint result = new SelectChannelEndPoint(channel, selectSet, key, maxIdleTime);
|
|
||||||
AsyncEndPoint endPoint = result;
|
|
||||||
|
|
||||||
// Detect if it is SSL, and wrap the connection if so
|
|
||||||
if ("wss".equals(holder.getURI().getScheme()))
|
|
||||||
{
|
|
||||||
SSLEngine sslEngine = newSslEngine(channel);
|
|
||||||
SslConnection sslConnection = new SslConnection(sslEngine, endPoint);
|
|
||||||
endPoint.setConnection(sslConnection);
|
|
||||||
endPoint = sslConnection.getSslEndPoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncConnection connection = selectSet.getManager().newConnection(channel, endPoint, holder);
|
|
||||||
endPoint.setConnection(connection);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment)
|
|
||||||
{
|
|
||||||
OldWebSocketClient.WebSocketFuture holder = (OldWebSocketClient.WebSocketFuture)attachment;
|
|
||||||
return new HandshakeConnection(endpoint, holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void endPointOpened(SelectChannelEndPoint endpoint)
|
|
||||||
{
|
|
||||||
// TODO expose on outer class ??
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
|
|
||||||
{
|
|
||||||
LOG.debug("upgrade {} -> {}", oldConnection, endpoint.getConnection());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void endPointClosed(SelectChannelEndPoint endpoint)
|
|
||||||
{
|
|
||||||
endpoint.getConnection().onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
|
|
||||||
{
|
|
||||||
if (!(attachment instanceof OldWebSocketClient.WebSocketFuture))
|
|
||||||
super.connectionFailed(channel, ex, attachment);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
__log.debug(ex);
|
|
||||||
OldWebSocketClient.WebSocketFuture future = (OldWebSocketClient.WebSocketFuture)attachment;
|
|
||||||
|
|
||||||
future.handshakeFailed(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Handshake Connection.
|
|
||||||
* Handles the connection until the handshake succeeds or fails.
|
|
||||||
*/
|
|
||||||
class HandshakeConnection extends AbstractConnection implements AsyncConnection
|
|
||||||
{
|
|
||||||
private final AsyncEndPoint _endp;
|
|
||||||
private final OldWebSocketClient.WebSocketFuture _future;
|
|
||||||
private final String _key;
|
|
||||||
private final HttpParser _parser;
|
|
||||||
private String _accept;
|
|
||||||
private String _error;
|
|
||||||
private ByteArrayBuffer _handshake;
|
|
||||||
|
|
||||||
public HandshakeConnection(AsyncEndPoint endpoint, OldWebSocketClient.WebSocketFuture future)
|
|
||||||
{
|
|
||||||
super(endpoint, System.currentTimeMillis());
|
|
||||||
_endp = endpoint;
|
|
||||||
_future = future;
|
|
||||||
|
|
||||||
byte[] bytes = new byte[16];
|
|
||||||
new Random().nextBytes(bytes);
|
|
||||||
_key = new String(B64Code.encode(bytes));
|
|
||||||
|
|
||||||
Buffers buffers = new SimpleBuffers(_buffers.getBuffer(), null);
|
|
||||||
_parser = new HttpParser(buffers, _endp, new HttpParser.EventHandler()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void startResponse(ByteBuffer version, int status, ByteBuffer reason) throws IOException
|
|
||||||
{
|
|
||||||
if (status != 101)
|
|
||||||
{
|
|
||||||
_error = "Bad response status " + status + " " + reason;
|
|
||||||
_endp.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void parsedHeader(ByteBuffer name, ByteBuffer value) throws IOException
|
|
||||||
{
|
|
||||||
if (__ACCEPT.equals(name))
|
|
||||||
_accept = value.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startRequest(ByteBuffer method, ByteBuffer url, ByteBuffer version) throws IOException
|
|
||||||
{
|
|
||||||
if (_error == null)
|
|
||||||
_error = "Bad response: " + method + " " + url + " " + version;
|
|
||||||
_endp.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void content(ByteBuffer ref) throws IOException
|
|
||||||
{
|
|
||||||
if (_error == null)
|
|
||||||
_error = "Bad response. " + ref.length() + "B of content?";
|
|
||||||
_endp.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean handshake()
|
|
||||||
{
|
|
||||||
if (_handshake==null)
|
|
||||||
{
|
|
||||||
String path = _future.getURI().getPath();
|
|
||||||
if (path == null || path.length() == 0)
|
|
||||||
path = "/";
|
|
||||||
|
|
||||||
if (_future.getURI().getRawQuery() != null)
|
|
||||||
path += "?" + _future.getURI().getRawQuery();
|
|
||||||
|
|
||||||
String origin = _future.getOrigin();
|
|
||||||
|
|
||||||
StringBuilder request = new StringBuilder(512);
|
|
||||||
request.append("GET ").append(path).append(" HTTP/1.1\r\n")
|
|
||||||
.append("Host: ").append(_future.getURI().getHost()).append(":")
|
|
||||||
.append(_future.getURI().getPort()).append("\r\n")
|
|
||||||
.append("Upgrade: websocket\r\n")
|
|
||||||
.append("Connection: Upgrade\r\n")
|
|
||||||
.append("Sec-WebSocket-Key: ")
|
|
||||||
.append(_key).append("\r\n");
|
|
||||||
|
|
||||||
if (origin != null)
|
|
||||||
request.append("Origin: ").append(origin).append("\r\n");
|
|
||||||
|
|
||||||
request.append("Sec-WebSocket-Version: ").append(WebSocketConnectionRFC6455.VERSION).append("\r\n");
|
|
||||||
|
|
||||||
if (_future.getProtocol() != null)
|
|
||||||
request.append("Sec-WebSocket-Protocol: ").append(_future.getProtocol()).append("\r\n");
|
|
||||||
|
|
||||||
Map<String, String> cookies = _future.getCookies();
|
|
||||||
if (cookies != null && cookies.size() > 0)
|
|
||||||
{
|
|
||||||
for (String cookie : cookies.keySet())
|
|
||||||
request.append("Cookie: ")
|
|
||||||
.append(QuotedStringTokenizer.quoteIfNeeded(cookie, HttpFields.__COOKIE_DELIM))
|
|
||||||
.append("=")
|
|
||||||
.append(QuotedStringTokenizer.quoteIfNeeded(cookies.get(cookie), HttpFields.__COOKIE_DELIM))
|
|
||||||
.append("\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
request.append("\r\n");
|
|
||||||
|
|
||||||
_handshake=new ByteArrayBuffer(request.toString(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO extensions
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int len = _handshake.length();
|
|
||||||
int flushed = _endp.flush(_handshake);
|
|
||||||
if (flushed<0)
|
|
||||||
throw new IOException("incomplete handshake");
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
_future.handshakeFailed(e);
|
|
||||||
}
|
|
||||||
return _handshake.length()==0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Connection handle() throws IOException
|
|
||||||
{
|
|
||||||
while (_endp.isOpen() && !_parser.isComplete())
|
|
||||||
{
|
|
||||||
if (_handshake==null || _handshake.length()>0)
|
|
||||||
if (!handshake())
|
|
||||||
return this;
|
|
||||||
|
|
||||||
if (!_parser.parseAvailable())
|
|
||||||
{
|
|
||||||
if (_endp.isInputShutdown())
|
|
||||||
_future.handshakeFailed(new IOException("Incomplete handshake response"));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_error == null)
|
|
||||||
{
|
|
||||||
if (_accept == null)
|
|
||||||
{
|
|
||||||
_error = "No Sec-WebSocket-Accept";
|
|
||||||
}
|
|
||||||
else if (!WebSocketConnectionRFC6455.hashKey(_key).equals(_accept))
|
|
||||||
{
|
|
||||||
_error = "Bad Sec-WebSocket-Accept";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WebSocketConnection connection = newWebSocketConnection();
|
|
||||||
|
|
||||||
ByteBuffer header = _parser.getHeaderBuffer();
|
|
||||||
if (header.hasContent())
|
|
||||||
connection.fillBuffersFrom(header);
|
|
||||||
_buffers.returnBuffer(header);
|
|
||||||
|
|
||||||
_future.onConnection(connection);
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_endp.close();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebSocketConnection newWebSocketConnection() throws IOException
|
|
||||||
{
|
|
||||||
return new WebSocketClientConnection(
|
|
||||||
_future._client.getFactory(),
|
|
||||||
_future.getWebSocket(),
|
|
||||||
_endp,
|
|
||||||
_buffers,
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
_future.getMaxIdleTime(),
|
|
||||||
_future.getProtocol(),
|
|
||||||
null,
|
|
||||||
WebSocketConnectionRFC6455.VERSION,
|
|
||||||
_future.getMaskGen());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onInputShutdown() throws IOException
|
|
||||||
{
|
|
||||||
_endp.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isIdle()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSuspended()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClose()
|
|
||||||
{
|
|
||||||
if (_error != null)
|
|
||||||
_future.handshakeFailed(new ProtocolException(_error));
|
|
||||||
else
|
|
||||||
_future.handshakeFailed(new EOFException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class WebSocketClientConnection extends WebSocketConnectionRFC6455
|
|
||||||
{
|
|
||||||
private final OldWebSocketClientFactory factory;
|
|
||||||
|
|
||||||
public WebSocketClientConnection(OldWebSocketClientFactory factory, WebSocket webSocket, EndPoint endPoint, WebSocketBuffers buffers, long timeStamp, int maxIdleTime, String protocol, List<Extension> extensions, int draftVersion, Masker maskGen) throws IOException
|
|
||||||
{
|
|
||||||
super(webSocket, endPoint, buffers, timeStamp, maxIdleTime, protocol, extensions, draftVersion, maskGen);
|
|
||||||
this.factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClose()
|
|
||||||
{
|
|
||||||
super.onClose();
|
|
||||||
factory.removeConnection(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -74,9 +74,15 @@ public class WebSocketClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final WebSocketClientFactory factory;
|
public static InetSocketAddress toSocketAddress(URI uri)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final WebSocketClientFactory factory;
|
||||||
private SocketAddress bindAddress;
|
private SocketAddress bindAddress;
|
||||||
|
|
||||||
private WebSocketPolicy policy;
|
private WebSocketPolicy policy;
|
||||||
|
|
||||||
public WebSocketClient(WebSocketClientFactory factory)
|
public WebSocketClient(WebSocketClientFactory factory)
|
||||||
|
@ -134,4 +140,10 @@ public class WebSocketClient
|
||||||
{
|
{
|
||||||
this.bindAddress = bindAddress;
|
this.bindAddress = bindAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProtocol(String protocol)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
||||||
|
|
||||||
public WebSocketClientFactory()
|
public WebSocketClientFactory()
|
||||||
{
|
{
|
||||||
this(null,null);
|
this(new QueuedThreadPool(),null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebSocketClientFactory(Executor threadPool)
|
public WebSocketClientFactory(Executor threadPool)
|
||||||
|
@ -41,14 +41,14 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
||||||
this(threadPool,null);
|
this(threadPool,null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebSocketClientFactory(Executor threadPool, SslContextFactory sslContextFactory)
|
public WebSocketClientFactory(Executor executor, SslContextFactory sslContextFactory)
|
||||||
{
|
{
|
||||||
if (threadPool == null)
|
if (executor == null)
|
||||||
{
|
{
|
||||||
threadPool = new QueuedThreadPool();
|
throw new IllegalArgumentException("Executor is required");
|
||||||
}
|
}
|
||||||
this.executor = threadPool;
|
this.executor = executor;
|
||||||
addBean(threadPool);
|
addBean(executor);
|
||||||
|
|
||||||
if (sslContextFactory != null)
|
if (sslContextFactory != null)
|
||||||
{
|
{
|
||||||
|
@ -117,7 +117,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
||||||
return selector;
|
return selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebSocketClient newSPDYClient()
|
public WebSocketClient newWebSocketClient()
|
||||||
{
|
{
|
||||||
return new WebSocketClient(this);
|
return new WebSocketClient(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,9 +103,8 @@ public class HandshakeConnection extends AbstractAsyncConnection implements Asyn
|
||||||
{
|
{
|
||||||
FutureCallback<ConnectFuture> callback = new FutureCallback<>();
|
FutureCallback<ConnectFuture> callback = new FutureCallback<>();
|
||||||
getEndPoint().write(future,callback,buf);
|
getEndPoint().write(future,callback,buf);
|
||||||
// TODO: block on read?
|
|
||||||
// TODO: read response & upgrade via async callback
|
// TODO: read response & upgrade via async callback
|
||||||
callback.get();
|
callback.get(); // TODO: block on read?
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,316 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2011 Intalio, Inc.
|
|
||||||
* ======================================================================
|
|
||||||
* 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.net.InetSocketAddress;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocketConnectionRFC6455;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket.Connection;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket.FrameConnection;
|
|
||||||
import org.eclipse.jetty.websocket.WebSocket.OnFrame;
|
|
||||||
import org.eclipse.jetty.websocket.client.OldWebSocketClient;
|
|
||||||
import org.eclipse.jetty.websocket.client.OldWebSocketClientFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is not a general purpose websocket client.
|
|
||||||
* It's only for testing the websocket server and is hardwired to a specific draft version of the protocol.
|
|
||||||
*/
|
|
||||||
public class TestClient implements WebSocket.OnFrame
|
|
||||||
{
|
|
||||||
private static OldWebSocketClientFactory __clientFactory = new OldWebSocketClientFactory();
|
|
||||||
private static boolean _verbose=false;
|
|
||||||
|
|
||||||
private static final Random __random = new Random();
|
|
||||||
|
|
||||||
private final String _host;
|
|
||||||
private final int _port;
|
|
||||||
private final String _protocol;
|
|
||||||
private final int _timeout;
|
|
||||||
|
|
||||||
private static boolean __quiet;
|
|
||||||
private static int __framesSent;
|
|
||||||
private static int __messagesSent;
|
|
||||||
private static AtomicInteger __framesReceived=new AtomicInteger();
|
|
||||||
private static AtomicInteger __messagesReceived=new AtomicInteger();
|
|
||||||
|
|
||||||
private static AtomicLong __totalTime=new AtomicLong();
|
|
||||||
private static AtomicLong __minDuration=new AtomicLong(Long.MAX_VALUE);
|
|
||||||
private static AtomicLong __maxDuration=new AtomicLong(Long.MIN_VALUE);
|
|
||||||
private static long __start;
|
|
||||||
private BlockingQueue<Long> _starts = new LinkedBlockingQueue<Long>();
|
|
||||||
int _messageBytes;
|
|
||||||
int _frames;
|
|
||||||
byte _opcode=-1;
|
|
||||||
private volatile WebSocket.FrameConnection _connection;
|
|
||||||
private final CountDownLatch _handshook = new CountDownLatch(1);
|
|
||||||
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
|
||||||
{
|
|
||||||
if (_verbose)
|
|
||||||
System.err.printf("%s#onHandshake %s %s\n",this.getClass().getSimpleName(),connection,connection.getClass().getSimpleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
_handshook.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onFrame(byte flags, byte opcode, byte[] data, int offset, int length)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_connection.isClose(opcode))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
__framesReceived.incrementAndGet();
|
|
||||||
_frames++;
|
|
||||||
_messageBytes+=length;
|
|
||||||
|
|
||||||
if (_opcode==-1)
|
|
||||||
_opcode=opcode;
|
|
||||||
|
|
||||||
if (_connection.isControl(opcode) || _connection.isMessageComplete(flags))
|
|
||||||
{
|
|
||||||
int recv =__messagesReceived.incrementAndGet();
|
|
||||||
Long start=_starts.poll();
|
|
||||||
|
|
||||||
if (start!=null)
|
|
||||||
{
|
|
||||||
long duration = System.nanoTime()-start.longValue();
|
|
||||||
long max=__maxDuration.get();
|
|
||||||
while(duration>max && !__maxDuration.compareAndSet(max,duration))
|
|
||||||
max=__maxDuration.get();
|
|
||||||
long min=__minDuration.get();
|
|
||||||
while(duration<min && !__minDuration.compareAndSet(min,duration))
|
|
||||||
min=__minDuration.get();
|
|
||||||
__totalTime.addAndGet(duration);
|
|
||||||
if (!__quiet)
|
|
||||||
System.out.printf("%d bytes from %s: frames=%d req=%d time=%.1fms opcode=0x%s\n",_messageBytes,_host,_frames,recv,((double)duration/1000000.0),TypeUtil.toHexString(_opcode));
|
|
||||||
}
|
|
||||||
_frames=0;
|
|
||||||
_messageBytes=0;
|
|
||||||
_opcode=-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onHandshake(FrameConnection connection)
|
|
||||||
{
|
|
||||||
_connection=connection;
|
|
||||||
_handshook.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestClient(String host, int port,String protocol, int timeoutMS) throws Exception
|
|
||||||
{
|
|
||||||
_host=host;
|
|
||||||
_port=port;
|
|
||||||
_protocol=protocol;
|
|
||||||
_timeout=timeoutMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void open() throws Exception
|
|
||||||
{
|
|
||||||
OldWebSocketClient client = new OldWebSocketClient(__clientFactory);
|
|
||||||
client.setProtocol(_protocol);
|
|
||||||
client.setMaxIdleTime(_timeout);
|
|
||||||
client.open(new URI("ws://"+_host+":"+_port+"/"),this).get(10,TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ping(byte opcode,byte[] data,int fragment) throws Exception
|
|
||||||
{
|
|
||||||
_starts.add(System.nanoTime());
|
|
||||||
|
|
||||||
int off=0;
|
|
||||||
int len=data.length;
|
|
||||||
if (fragment>0&& len>fragment)
|
|
||||||
len=fragment;
|
|
||||||
__messagesSent++;
|
|
||||||
while(off<data.length)
|
|
||||||
{
|
|
||||||
__framesSent++;
|
|
||||||
byte flags= (byte)(off+len==data.length?0x8:0);
|
|
||||||
byte op=(byte)(off==0?opcode:WebSocketConnectionRFC6455.OP_CONTINUATION);
|
|
||||||
|
|
||||||
if (_verbose)
|
|
||||||
System.err.printf("%s#sendFrame %s|%s %s\n",this.getClass().getSimpleName(),TypeUtil.toHexString(flags),TypeUtil.toHexString(op),TypeUtil.toHexString(data,off,len));
|
|
||||||
|
|
||||||
_connection.sendFrame(flags,op,data,off,len);
|
|
||||||
|
|
||||||
off+=len;
|
|
||||||
if(data.length-off>len)
|
|
||||||
len=data.length-off;
|
|
||||||
if (fragment>0&& len>fragment)
|
|
||||||
len=fragment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect() throws Exception
|
|
||||||
{
|
|
||||||
if (_connection!=null)
|
|
||||||
{
|
|
||||||
_connection.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void usage(String[] args)
|
|
||||||
{
|
|
||||||
System.err.println("ERROR: "+Arrays.asList(args));
|
|
||||||
System.err.println("USAGE: java -cp CLASSPATH "+TestClient.class+" [ OPTIONS ]");
|
|
||||||
System.err.println(" -h|--host HOST (default localhost)");
|
|
||||||
System.err.println(" -p|--port PORT (default 8080)");
|
|
||||||
System.err.println(" -b|--binary");
|
|
||||||
System.err.println(" -v|--verbose");
|
|
||||||
System.err.println(" -q|--quiet");
|
|
||||||
System.err.println(" -c|--count n (default 10)");
|
|
||||||
System.err.println(" -s|--size n (default 64)");
|
|
||||||
System.err.println(" -f|--fragment n (default 4000) ");
|
|
||||||
System.err.println(" -P|--protocol echo|echo-assemble|echo-fragment|echo-broadcast");
|
|
||||||
System.err.println(" -C|--clients n (default 1) ");
|
|
||||||
System.err.println(" -d|--delay n (default 1000ms) ");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception
|
|
||||||
{
|
|
||||||
__clientFactory.start();
|
|
||||||
|
|
||||||
String host="localhost";
|
|
||||||
int port=8080;
|
|
||||||
String protocol=null;
|
|
||||||
int count=10;
|
|
||||||
int size=64;
|
|
||||||
int fragment=4000;
|
|
||||||
boolean binary=false;
|
|
||||||
int clients=1;
|
|
||||||
int delay=1000;
|
|
||||||
|
|
||||||
for (int i=0;i<args.length;i++)
|
|
||||||
{
|
|
||||||
String a=args[i];
|
|
||||||
if ("-p".equals(a)||"--port".equals(a))
|
|
||||||
port=Integer.parseInt(args[++i]);
|
|
||||||
else if ("-h".equals(a)||"--host".equals(a))
|
|
||||||
host=args[++i];
|
|
||||||
else if ("-c".equals(a)||"--count".equals(a))
|
|
||||||
count=Integer.parseInt(args[++i]);
|
|
||||||
else if ("-s".equals(a)||"--size".equals(a))
|
|
||||||
size=Integer.parseInt(args[++i]);
|
|
||||||
else if ("-f".equals(a)||"--fragment".equals(a))
|
|
||||||
fragment=Integer.parseInt(args[++i]);
|
|
||||||
else if ("-P".equals(a)||"--protocol".equals(a))
|
|
||||||
protocol=args[++i];
|
|
||||||
else if ("-v".equals(a)||"--verbose".equals(a))
|
|
||||||
_verbose=true;
|
|
||||||
else if ("-b".equals(a)||"--binary".equals(a))
|
|
||||||
binary=true;
|
|
||||||
else if ("-C".equals(a)||"--clients".equals(a))
|
|
||||||
clients=Integer.parseInt(args[++i]);
|
|
||||||
else if ("-d".equals(a)||"--delay".equals(a))
|
|
||||||
delay=Integer.parseInt(args[++i]);
|
|
||||||
else if ("-q".equals(a)||"--quiet".equals(a))
|
|
||||||
__quiet=true;
|
|
||||||
else if (a.startsWith("-"))
|
|
||||||
usage(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TestClient[] client = new TestClient[clients];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
__start=System.currentTimeMillis();
|
|
||||||
protocol=protocol==null?"echo":protocol;
|
|
||||||
|
|
||||||
for (int i=0;i<clients;i++)
|
|
||||||
{
|
|
||||||
client[i]=new TestClient(host,port,protocol==null?null:protocol,60000);
|
|
||||||
client[i].open();
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Jetty WebSocket PING "+host+":"+port+
|
|
||||||
" ("+ new InetSocketAddress(host,port)+") "+clients+" clients "+protocol);
|
|
||||||
|
|
||||||
|
|
||||||
for (int p=0;p<count;p++)
|
|
||||||
{
|
|
||||||
long next = System.currentTimeMillis()+delay;
|
|
||||||
|
|
||||||
byte opcode=binary?WebSocketConnectionRFC6455.OP_BINARY:WebSocketConnectionRFC6455.OP_TEXT;
|
|
||||||
|
|
||||||
byte data[]=null;
|
|
||||||
|
|
||||||
if (opcode==WebSocketConnectionRFC6455.OP_TEXT)
|
|
||||||
{
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
while (b.length()<size)
|
|
||||||
b.append('A'+__random.nextInt(26));
|
|
||||||
data=b.toString().getBytes(StringUtil.__UTF8);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data= new byte[size];
|
|
||||||
__random.nextBytes(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0;i<clients;i++)
|
|
||||||
client[i].ping(opcode,data,opcode==WebSocketConnectionRFC6455.OP_PING?-1:fragment);
|
|
||||||
|
|
||||||
while(System.currentTimeMillis()<next)
|
|
||||||
Thread.sleep(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
for (int i=0;i<clients;i++)
|
|
||||||
if (client[i]!=null)
|
|
||||||
client[i].disconnect();
|
|
||||||
|
|
||||||
long duration=System.currentTimeMillis()-__start;
|
|
||||||
System.out.println("--- "+host+" websocket ping statistics using "+clients+" connection"+(clients>1?"s":"")+" ---");
|
|
||||||
System.out.printf("%d/%d frames sent/recv, %d/%d mesg sent/recv, time %dms %dm/s %.2fbps%n",
|
|
||||||
__framesSent,__framesReceived.get(),
|
|
||||||
__messagesSent,__messagesReceived.get(),
|
|
||||||
duration,(1000L*__messagesReceived.get()/duration),
|
|
||||||
1000.0D*__messagesReceived.get()*8*size/duration/1024/1024);
|
|
||||||
System.out.printf("rtt min/ave/max = %.3f/%.3f/%.3f ms\n",__minDuration.get()/1000000.0,__messagesReceived.get()==0?0.0:(__totalTime.get()/__messagesReceived.get()/1000000.0),__maxDuration.get()/1000000.0);
|
|
||||||
|
|
||||||
__clientFactory.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,16 +15,12 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.jetty.websocket.client;
|
package org.eclipse.jetty.websocket.client;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Exchanger;
|
import java.util.concurrent.Exchanger;
|
||||||
|
@ -37,10 +33,12 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketConnection;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketListener;
|
||||||
import org.eclipse.jetty.websocket.client.blockhead.BlockheadServer;
|
import org.eclipse.jetty.websocket.client.blockhead.BlockheadServer;
|
||||||
import org.eclipse.jetty.websocket.masks.ZeroMasker;
|
import org.eclipse.jetty.websocket.client.blockhead.BlockheadServer.ServerConnection;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -48,63 +46,84 @@ import org.junit.Test;
|
||||||
|
|
||||||
public class WebSocketClientTest
|
public class WebSocketClientTest
|
||||||
{
|
{
|
||||||
|
public static class TrackingSocket extends WebSocketAdapter
|
||||||
|
{
|
||||||
|
public AtomicBoolean open = new AtomicBoolean(false);
|
||||||
|
public AtomicInteger close = new AtomicInteger(-1);
|
||||||
|
public StringBuilder closeMessage = new StringBuilder();
|
||||||
|
public CountDownLatch openLatch = new CountDownLatch(1);
|
||||||
|
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
|
public CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
|
public BlockingQueue<String> messageQueue = new BlockingArrayQueue<String>();
|
||||||
|
|
||||||
|
public void assertClose(int expectedStatusCode, String expectedReason)
|
||||||
|
{
|
||||||
|
assertCloseCode(expectedStatusCode);
|
||||||
|
assertCloseReason(expectedReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertCloseCode(int expectedCode)
|
||||||
|
{
|
||||||
|
Assert.assertThat("Close Code",close.get(),is(expectedCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCloseReason(String expectedReason)
|
||||||
|
{
|
||||||
|
Assert.assertThat("Close Reaosn",closeMessage.toString(),is(expectedReason));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertIsOpen()
|
||||||
|
{
|
||||||
|
assertWasOpened();
|
||||||
|
assertNotClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertNotClosed()
|
||||||
|
{
|
||||||
|
Assert.assertThat("Close Code",close.get(),is(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertNotOpened()
|
||||||
|
{
|
||||||
|
Assert.assertThat("Opened State",open.get(),is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertWasOpened()
|
||||||
|
{
|
||||||
|
Assert.assertThat("Opened State",open.get(),is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketBinary(byte[] payload, int offset, int len)
|
||||||
|
{
|
||||||
|
dataLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketClose(int statusCode, String reason)
|
||||||
|
{
|
||||||
|
close.set(statusCode);
|
||||||
|
closeMessage.append(reason);
|
||||||
|
closeLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketConnect(WebSocketConnection connection)
|
||||||
|
{
|
||||||
|
open.set(true);
|
||||||
|
openLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketText(String message)
|
||||||
|
{
|
||||||
|
dataLatch.countDown();
|
||||||
|
messageQueue.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private BlockheadServer server;
|
private BlockheadServer server;
|
||||||
|
|
||||||
private void accept(Socket connection) throws IOException
|
|
||||||
{
|
|
||||||
String key = "not sent";
|
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
|
||||||
for (String line = in.readLine(); line != null; line = in.readLine())
|
|
||||||
{
|
|
||||||
if (line.length() == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (line.startsWith("Sec-WebSocket-Key:"))
|
|
||||||
{
|
|
||||||
key = line.substring(18).trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
connection.getOutputStream().write(
|
|
||||||
("HTTP/1.1 101 Upgrade\r\n" + "Sec-WebSocket-Accept: " + WebSocketConnectionRFC6455.hashKey(key) + "\r\n" + "\r\n").getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void respondToClient(Socket connection, String serverResponse) throws IOException
|
|
||||||
{
|
|
||||||
InputStream in = null;
|
|
||||||
InputStreamReader isr = null;
|
|
||||||
BufferedReader buf = null;
|
|
||||||
OutputStream out = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
in = connection.getInputStream();
|
|
||||||
isr = new InputStreamReader(in);
|
|
||||||
buf = new BufferedReader(isr);
|
|
||||||
String line;
|
|
||||||
while ((line = buf.readLine()) != null)
|
|
||||||
{
|
|
||||||
// System.err.println(line);
|
|
||||||
if (line.length() == 0)
|
|
||||||
{
|
|
||||||
// Got the "\r\n" line.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// System.out.println("[Server-Out] " + serverResponse);
|
|
||||||
out = connection.getOutputStream();
|
|
||||||
out.write(serverResponse.getBytes());
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
IO.close(buf);
|
|
||||||
IO.close(isr);
|
|
||||||
IO.close(in);
|
|
||||||
IO.close(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void startServer() throws Exception
|
public void startServer() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -121,63 +140,44 @@ public class WebSocketClientTest
|
||||||
@Test
|
@Test
|
||||||
public void testAsyncConnectionRefused() throws Exception
|
public void testAsyncConnectionRefused() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:1"),new WebSocket()
|
// Intentionally bad port
|
||||||
{
|
URI wsUri = new URI("ws://127.0.0.1:1");
|
||||||
public void onClose(int closeCode, String message)
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
|
||||||
{
|
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Throwable error = null;
|
Throwable error = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
future.get(1,TimeUnit.SECONDS);
|
future.get(1,TimeUnit.SECONDS);
|
||||||
Assert.fail();
|
Assert.fail("Expected ExecutionException");
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
catch (ExecutionException e)
|
||||||
{
|
{
|
||||||
error = e.getCause();
|
error = e.getCause();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertFalse(open.get());
|
wsocket.assertNotOpened();
|
||||||
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
|
wsocket.assertCloseCode(StatusCode.NO_CLOSE);
|
||||||
Assert.assertTrue(error instanceof ConnectException);
|
Assert.assertTrue(error instanceof ConnectException);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBadHandshake() throws Exception
|
public void testBadHandshake() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),new WebSocket()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket connection = _server.accept();
|
ServerConnection connection = server.accept();
|
||||||
respondToClient(connection,"HTTP/1.1 404 NOT FOUND\r\n\r\n");
|
connection.respond("HTTP/1.1 404 NOT FOUND\r\n\r\n");
|
||||||
|
|
||||||
Throwable error = null;
|
Throwable error = null;
|
||||||
try
|
try
|
||||||
|
@ -190,8 +190,8 @@ public class WebSocketClientTest
|
||||||
error = e.getCause();
|
error = e.getCause();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertFalse(open.get());
|
wsocket.assertNotOpened();
|
||||||
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,close.get());
|
wsocket.assertCloseCode(StatusCode.PROTOCOL);
|
||||||
Assert.assertTrue(error instanceof IOException);
|
Assert.assertTrue(error instanceof IOException);
|
||||||
Assert.assertTrue(error.getMessage().indexOf("404 NOT FOUND") > 0);
|
Assert.assertTrue(error.getMessage().indexOf("404 NOT FOUND") > 0);
|
||||||
|
|
||||||
|
@ -200,25 +200,16 @@ public class WebSocketClientTest
|
||||||
@Test
|
@Test
|
||||||
public void testBadUpgrade() throws Exception
|
public void testBadUpgrade() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),new WebSocket()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket connection = _server.accept();
|
ServerConnection connection = server.accept();
|
||||||
respondToClient(connection,"HTTP/1.1 101 Upgrade\r\n" + "Sec-WebSocket-Accept: rubbish\r\n" + "\r\n");
|
connection.respond("HTTP/1.1 101 Upgrade\r\n" + "Sec-WebSocket-Accept: rubbish\r\n" + "\r\n");
|
||||||
|
|
||||||
Throwable error = null;
|
Throwable error = null;
|
||||||
try
|
try
|
||||||
|
@ -230,86 +221,86 @@ public class WebSocketClientTest
|
||||||
{
|
{
|
||||||
error = e.getCause();
|
error = e.getCause();
|
||||||
}
|
}
|
||||||
Assert.assertFalse(open.get());
|
|
||||||
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,close.get());
|
wsocket.assertNotOpened();
|
||||||
|
wsocket.assertCloseCode(StatusCode.PROTOCOL);
|
||||||
Assert.assertTrue(error instanceof IOException);
|
Assert.assertTrue(error instanceof IOException);
|
||||||
Assert.assertTrue(error.getMessage().indexOf("Bad Sec-WebSocket-Accept") >= 0);
|
Assert.assertThat("Error Message",error.getMessage(),containsString("Bad Sec-WebSocket-Accept"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBadURL() throws Exception
|
public void testBadURL() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
|
||||||
boolean bad = false;
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
client.open(new URI("http://localhost:8080"),new WebSocket()
|
// Intentionally bad scheme in URI
|
||||||
{
|
URI wsUri = new URI("http://localhost:8080");
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
client.connect(wsUri,wsocket); // should toss exception
|
||||||
{
|
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Assert.fail();
|
Assert.fail("Expected IllegalArgumentException");
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e)
|
catch (IllegalArgumentException e)
|
||||||
{
|
{
|
||||||
bad = true;
|
// expected path
|
||||||
|
wsocket.assertNotOpened();
|
||||||
}
|
}
|
||||||
Assert.assertTrue(bad);
|
|
||||||
Assert.assertFalse(open.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockReceiving() throws Exception
|
public void testBlockReceiving() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
client.setMaxIdleTime(60000);
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
client.getPolicy().setMaxIdleTime(60000);
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
final AtomicBoolean open = new AtomicBoolean(false);
|
||||||
final AtomicInteger close = new AtomicInteger();
|
final AtomicInteger close = new AtomicInteger();
|
||||||
final CountDownLatch _latch = new CountDownLatch(1);
|
final CountDownLatch _latch = new CountDownLatch(1);
|
||||||
final StringBuilder closeMessage = new StringBuilder();
|
final StringBuilder closeMessage = new StringBuilder();
|
||||||
final Exchanger<String> exchanger = new Exchanger<String>();
|
final Exchanger<String> exchanger = new Exchanger<String>();
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),new WebSocket.OnTextMessage()
|
|
||||||
|
WebSocketListener socket = new WebSocketAdapter()
|
||||||
{
|
{
|
||||||
public void onClose(int closeCode, String message)
|
@Override
|
||||||
|
public void onWebSocketClose(int statusCode, String reason)
|
||||||
{
|
{
|
||||||
close.set(closeCode);
|
close.set(statusCode);
|
||||||
closeMessage.append(message);
|
closeMessage.append(reason);
|
||||||
_latch.countDown();
|
_latch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMessage(String data)
|
@Override
|
||||||
|
public void onWebSocketConnect(WebSocketConnection connection)
|
||||||
|
{
|
||||||
|
open.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketText(String message)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
exchanger.exchange(data);
|
exchanger.exchange(message);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e)
|
catch (InterruptedException e)
|
||||||
{
|
{
|
||||||
// e.printStackTrace();
|
// e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,socket);
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket socket = _server.accept();
|
ServerConnection sconnection = server.accept();
|
||||||
socket.setSoTimeout(60000);
|
sconnection.setSoTimeout(60000);
|
||||||
accept(socket);
|
|
||||||
|
|
||||||
WebSocket.Connection connection = future.get(250,TimeUnit.MILLISECONDS);
|
WebSocketConnection connection = future.get(250,TimeUnit.MILLISECONDS);
|
||||||
Assert.assertNotNull(connection);
|
Assert.assertNotNull(connection);
|
||||||
Assert.assertTrue(open.get());
|
Assert.assertTrue(open.get());
|
||||||
Assert.assertEquals(0,close.get());
|
Assert.assertEquals(0,close.get());
|
||||||
|
@ -362,8 +353,8 @@ public class WebSocketClientTest
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
for (int i = 0; i < messages; i++)
|
for (int i = 0; i < messages; i++)
|
||||||
{
|
{
|
||||||
socket.getOutputStream().write(send,0,send.length);
|
sconnection.write(send,0,send.length);
|
||||||
socket.getOutputStream().flush();
|
sconnection.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (consumer.isAlive())
|
while (consumer.isAlive())
|
||||||
|
@ -379,9 +370,9 @@ public class WebSocketClientTest
|
||||||
|
|
||||||
// Close with code
|
// Close with code
|
||||||
start = System.currentTimeMillis();
|
start = System.currentTimeMillis();
|
||||||
socket.getOutputStream().write(new byte[]
|
sconnection.write(new byte[]
|
||||||
{ (byte)0x88, (byte)0x02, (byte)4, (byte)87 },0,4);
|
{ (byte)0x88, (byte)0x02, (byte)4, (byte)87 },0,4);
|
||||||
socket.getOutputStream().flush();
|
sconnection.flush();
|
||||||
|
|
||||||
_latch.await(10,TimeUnit.SECONDS);
|
_latch.await(10,TimeUnit.SECONDS);
|
||||||
Assert.assertTrue((System.currentTimeMillis() - start) < 5000);
|
Assert.assertTrue((System.currentTimeMillis() - start) < 5000);
|
||||||
|
@ -392,37 +383,22 @@ public class WebSocketClientTest
|
||||||
@Test
|
@Test
|
||||||
public void testBlockSending() throws Exception
|
public void testBlockSending() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
client.setMaxIdleTime(10000);
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
client.getPolicy().setMaxIdleTime(10000);
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
final CountDownLatch _latch = new CountDownLatch(1);
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),new WebSocket.OnTextMessage()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
_latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMessage(String data)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
final ServerConnection ssocket = server.accept();
|
||||||
{
|
ssocket.upgrade();
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final Socket socket = _server.accept();
|
WebSocketConnection connection = future.get(250,TimeUnit.MILLISECONDS);
|
||||||
accept(socket);
|
|
||||||
|
|
||||||
WebSocket.Connection connection = future.get(250,TimeUnit.MILLISECONDS);
|
|
||||||
Assert.assertNotNull(connection);
|
Assert.assertNotNull(connection);
|
||||||
Assert.assertTrue(open.get());
|
wsocket.assertWasOpened();
|
||||||
Assert.assertEquals(0,close.get());
|
wsocket.assertNotClosed();
|
||||||
|
|
||||||
final int messages = 200000;
|
final int messages = 200000;
|
||||||
final AtomicLong totalB = new AtomicLong();
|
final AtomicLong totalB = new AtomicLong();
|
||||||
|
@ -442,7 +418,7 @@ public class WebSocketClientTest
|
||||||
while (len >= 0)
|
while (len >= 0)
|
||||||
{
|
{
|
||||||
totalB.addAndGet(len);
|
totalB.addAndGet(len);
|
||||||
len = socket.getInputStream().read(recv,0,recv.length);
|
len = ssocket.getInputStream().read(recv,0,recv.length);
|
||||||
Thread.sleep(10);
|
Thread.sleep(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -463,7 +439,7 @@ public class WebSocketClientTest
|
||||||
String mesg = "This is a test message to send";
|
String mesg = "This is a test message to send";
|
||||||
for (int i = 0; i < messages; i++)
|
for (int i = 0; i < messages; i++)
|
||||||
{
|
{
|
||||||
connection.sendMessage(mesg);
|
connection.write(mesg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duration for the write phase
|
// Duration for the write phase
|
||||||
|
@ -484,210 +460,136 @@ public class WebSocketClientTest
|
||||||
@Test
|
@Test
|
||||||
public void testConnectionNotAccepted() throws Exception
|
public void testConnectionNotAccepted() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort),new WebSocket()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
open.set(true);
|
|
||||||
}
|
// Intentionally not accept incoming socket.
|
||||||
});
|
// server.accept();
|
||||||
|
|
||||||
Throwable error = null;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
future.get(250,TimeUnit.MILLISECONDS);
|
future.get(250,TimeUnit.MILLISECONDS);
|
||||||
Assert.fail();
|
Assert.fail("Should have Timed Out");
|
||||||
}
|
}
|
||||||
catch (TimeoutException e)
|
catch (TimeoutException e)
|
||||||
{
|
{
|
||||||
error = e;
|
// Expected Path
|
||||||
|
wsocket.assertNotOpened();
|
||||||
|
wsocket.assertCloseCode(StatusCode.NO_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertFalse(open.get());
|
|
||||||
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
|
|
||||||
Assert.assertTrue(error instanceof TimeoutException);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectionTimeout() throws Exception
|
public void testConnectionTimeout() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort),new WebSocket()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Assert.assertNotNull(_server.accept());
|
ServerConnection ssocket = server.accept();
|
||||||
|
Assert.assertNotNull(ssocket);
|
||||||
|
// Intentionally don't upgrade
|
||||||
|
// ssocket.upgrade();
|
||||||
|
|
||||||
Throwable error = null;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
future.get(250,TimeUnit.MILLISECONDS);
|
future.get(250,TimeUnit.MILLISECONDS);
|
||||||
Assert.fail();
|
Assert.fail("Expected Timeout Exception");
|
||||||
}
|
}
|
||||||
catch (TimeoutException e)
|
catch (TimeoutException e)
|
||||||
{
|
{
|
||||||
error = e;
|
// Expected path
|
||||||
|
wsocket.assertNotOpened();
|
||||||
|
wsocket.assertCloseCode(StatusCode.NO_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertFalse(open.get());
|
|
||||||
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
|
|
||||||
Assert.assertTrue(error instanceof TimeoutException);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIdle() throws Exception
|
public void testIdle() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
client.setMaxIdleTime(500);
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
client.getPolicy().setMaxIdleTime(500);
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
final CountDownLatch _latch = new CountDownLatch(1);
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),new WebSocket()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
_latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket socket = _server.accept();
|
ServerConnection ssocket = server.accept();
|
||||||
accept(socket);
|
ssocket.upgrade();
|
||||||
|
|
||||||
WebSocket.Connection connection = future.get(250,TimeUnit.MILLISECONDS);
|
WebSocketConnection connection = future.get(250,TimeUnit.MILLISECONDS);
|
||||||
Assert.assertNotNull(connection);
|
Assert.assertNotNull(connection);
|
||||||
Assert.assertTrue(open.get());
|
wsocket.assertWasOpened();
|
||||||
Assert.assertEquals(0,close.get());
|
wsocket.assertNotClosed();
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
_latch.await(10,TimeUnit.SECONDS);
|
wsocket.closeLatch.await(10,TimeUnit.SECONDS);
|
||||||
Assert.assertTrue((System.currentTimeMillis() - start) < 5000);
|
Assert.assertTrue((System.currentTimeMillis() - start) < 5000);
|
||||||
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NORMAL,close.get());
|
wsocket.assertCloseCode(StatusCode.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessageBiggerThanBufferSize() throws Exception
|
public void testMessageBiggerThanBufferSize() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClientFactory factory = new OldWebSocketClientFactory();
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
|
||||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
|
||||||
int bufferSize = 512;
|
int bufferSize = 512;
|
||||||
OldWebSocketClientFactory factory = new OldWebSocketClientFactory(threadPool,new ZeroMasker(),bufferSize);
|
factory.getPolicy().setBufferSize(512);
|
||||||
threadPool.start();
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
factory.start();
|
|
||||||
OldWebSocketClient client = new OldWebSocketClient(factory);
|
|
||||||
|
|
||||||
final CountDownLatch openLatch = new CountDownLatch(1);
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
|
||||||
WebSocket.OnTextMessage websocket = new WebSocket.OnTextMessage()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMessage(String data)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
// System.out.println("data = " + data);
|
|
||||||
dataLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
ServerConnection ssocket = server.accept();
|
||||||
{
|
ssocket.upgrade();
|
||||||
openLatch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),websocket);
|
|
||||||
|
|
||||||
Socket socket = _server.accept();
|
Assert.assertTrue(wsocket.openLatch.await(1,TimeUnit.SECONDS));
|
||||||
accept(socket);
|
|
||||||
|
|
||||||
Assert.assertTrue(openLatch.await(1,TimeUnit.SECONDS));
|
int length = bufferSize + (bufferSize / 2); // 1.5 times buffer size
|
||||||
OutputStream serverOutput = socket.getOutputStream();
|
ssocket.write(0x80 | 0x01); // FIN + TEXT
|
||||||
|
ssocket.write(0x7E); // No MASK and 2 bytes length
|
||||||
int length = bufferSize + (bufferSize / 2);
|
ssocket.write(length >> 8); // first length byte
|
||||||
serverOutput.write(0x80 | 0x01); // FIN + TEXT
|
ssocket.write(length & 0xFF); // second length byte
|
||||||
serverOutput.write(0x7E); // No MASK and 2 bytes length
|
|
||||||
serverOutput.write(length >> 8); // first length byte
|
|
||||||
serverOutput.write(length & 0xFF); // second length byte
|
|
||||||
for (int i = 0; i < length; ++i)
|
for (int i = 0; i < length; ++i)
|
||||||
{
|
{
|
||||||
serverOutput.write('x');
|
ssocket.write('x');
|
||||||
}
|
}
|
||||||
serverOutput.flush();
|
ssocket.flush();
|
||||||
|
|
||||||
Assert.assertTrue(dataLatch.await(1000,TimeUnit.SECONDS));
|
Assert.assertTrue(wsocket.dataLatch.await(1000,TimeUnit.SECONDS));
|
||||||
|
|
||||||
factory.stop();
|
|
||||||
threadPool.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotIdle() throws Exception
|
public void testNotIdle() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
client.setMaxIdleTime(500);
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
client.getPolicy().setMaxIdleTime(500);
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
final CountDownLatch _latch = new CountDownLatch(1);
|
|
||||||
final BlockingQueue<String> queue = new BlockingArrayQueue<String>();
|
|
||||||
final StringBuilder closeMessage = new StringBuilder();
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),new WebSocket.OnTextMessage()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
closeMessage.append(message);
|
|
||||||
_latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMessage(String data)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
queue.add(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
ServerConnection ssocket = server.accept();
|
||||||
{
|
ssocket.upgrade();
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket socket = _server.accept();
|
WebSocketConnection connection = future.get(250,TimeUnit.MILLISECONDS);
|
||||||
accept(socket);
|
|
||||||
|
|
||||||
WebSocket.Connection connection = future.get(250,TimeUnit.MILLISECONDS);
|
|
||||||
Assert.assertNotNull(connection);
|
Assert.assertNotNull(connection);
|
||||||
Assert.assertTrue(open.get());
|
|
||||||
Assert.assertEquals(0,close.get());
|
wsocket.assertIsOpen();
|
||||||
|
|
||||||
// Send some messages client to server
|
// Send some messages client to server
|
||||||
byte[] recv = new byte[1024];
|
byte[] recv = new byte[1024];
|
||||||
|
@ -695,8 +597,8 @@ public class WebSocketClientTest
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
Thread.sleep(250);
|
Thread.sleep(250);
|
||||||
connection.sendMessage("Hello");
|
connection.write("Hello");
|
||||||
len = socket.getInputStream().read(recv,0,recv.length);
|
len = ssocket.getInputStream().read(recv,0,recv.length);
|
||||||
Assert.assertTrue(len > 0);
|
Assert.assertTrue(len > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,65 +609,53 @@ public class WebSocketClientTest
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
Thread.sleep(250);
|
Thread.sleep(250);
|
||||||
socket.getOutputStream().write(send,0,send.length);
|
ssocket.write(send,0,send.length);
|
||||||
socket.getOutputStream().flush();
|
ssocket.flush();
|
||||||
Assert.assertEquals("Hi",queue.poll(1,TimeUnit.SECONDS));
|
Assert.assertEquals("Hi",wsocket.messageQueue.poll(1,TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close with code
|
// Close with code
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
socket.getOutputStream().write(new byte[]
|
ssocket.write(new byte[]
|
||||||
{ (byte)0x88, (byte)0x02, (byte)4, (byte)87 },0,4);
|
{ (byte)0x88, (byte)0x02, (byte)4, (byte)87 },0,4);
|
||||||
socket.getOutputStream().flush();
|
ssocket.flush();
|
||||||
|
|
||||||
_latch.await(10,TimeUnit.SECONDS);
|
wsocket.closeLatch.await(10,TimeUnit.SECONDS);
|
||||||
Assert.assertTrue((System.currentTimeMillis() - start) < 5000);
|
Assert.assertTrue((System.currentTimeMillis() - start) < 5000);
|
||||||
Assert.assertEquals(1002,close.get());
|
wsocket.assertClose(StatusCode.PROTOCOL,"Invalid close code 1111");
|
||||||
Assert.assertEquals("Invalid close code 1111",closeMessage.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpgradeThenTCPClose() throws Exception
|
public void testUpgradeThenTCPClose() throws Exception
|
||||||
{
|
{
|
||||||
OldWebSocketClient client = new OldWebSocketClient(_factory);
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
|
||||||
final AtomicBoolean open = new AtomicBoolean();
|
TrackingSocket wsocket = new TrackingSocket();
|
||||||
final AtomicInteger close = new AtomicInteger();
|
|
||||||
final CountDownLatch _latch = new CountDownLatch(1);
|
|
||||||
Future<WebSocket.Connection> future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"),new WebSocket()
|
|
||||||
{
|
|
||||||
public void onClose(int closeCode, String message)
|
|
||||||
{
|
|
||||||
close.set(closeCode);
|
|
||||||
_latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Connection connection)
|
URI wsUri = new URI("ws://127.0.0.1:" + server.getPort() + "/");
|
||||||
{
|
Future<WebSocketConnection> future = client.connect(wsUri,wsocket);
|
||||||
open.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket socket = _server.accept();
|
ServerConnection ssocket = server.accept();
|
||||||
accept(socket);
|
ssocket.upgrade();
|
||||||
|
|
||||||
WebSocket.Connection connection = future.get(250,TimeUnit.MILLISECONDS);
|
WebSocketConnection connection = future.get(250,TimeUnit.MILLISECONDS);
|
||||||
Assert.assertNotNull(connection);
|
Assert.assertNotNull(connection);
|
||||||
Assert.assertTrue(open.get());
|
|
||||||
Assert.assertEquals(0,close.get());
|
|
||||||
|
|
||||||
socket.close();
|
wsocket.assertIsOpen();
|
||||||
_latch.await(10,TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
|
ssocket.close();
|
||||||
|
wsocket.openLatch.await(10,TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
wsocket.assertCloseCode(StatusCode.NO_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testURIWithDefaultPort() throws Exception
|
public void testURIWithDefaultPort() throws Exception
|
||||||
{
|
{
|
||||||
URI uri = new URI("ws://localhost");
|
URI uri = new URI("ws://localhost");
|
||||||
InetSocketAddress addr = OldWebSocketClient.toSocketAddress(uri);
|
|
||||||
|
InetSocketAddress addr = WebSocketClient.toSocketAddress(uri);
|
||||||
Assert.assertThat("URI (" + uri + ").host",addr.getHostName(),is("localhost"));
|
Assert.assertThat("URI (" + uri + ").host",addr.getHostName(),is("localhost"));
|
||||||
Assert.assertThat("URI (" + uri + ").port",addr.getPort(),is(80));
|
Assert.assertThat("URI (" + uri + ").port",addr.getPort(),is(80));
|
||||||
}
|
}
|
||||||
|
@ -774,7 +664,7 @@ public class WebSocketClientTest
|
||||||
public void testURIWithDefaultWSSPort() throws Exception
|
public void testURIWithDefaultWSSPort() throws Exception
|
||||||
{
|
{
|
||||||
URI uri = new URI("wss://localhost");
|
URI uri = new URI("wss://localhost");
|
||||||
InetSocketAddress addr = OldWebSocketClient.toSocketAddress(uri);
|
InetSocketAddress addr = WebSocketClient.toSocketAddress(uri);
|
||||||
Assert.assertThat("URI (" + uri + ").host",addr.getHostName(),is("localhost"));
|
Assert.assertThat("URI (" + uri + ").host",addr.getHostName(),is("localhost"));
|
||||||
Assert.assertThat("URI (" + uri + ").port",addr.getPort(),is(443));
|
Assert.assertThat("URI (" + uri + ").port",addr.getPort(),is(443));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,183 @@
|
||||||
package org.eclipse.jetty.websocket.client.blockhead;
|
package org.eclipse.jetty.websocket.client.blockhead;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.IO;
|
||||||
|
import org.eclipse.jetty.websocket.api.AcceptHash;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A overly simplistic websocket server used during testing.
|
* A overly simplistic websocket server used during testing.
|
||||||
* <p>
|
* <p>
|
||||||
* This is not meant to be performant or accurate.
|
* This is not meant to be performant or accurate. In fact, having the server misbehave is a useful trait during testing.
|
||||||
* In fact, having the server misbehave is a useful trait during testing.
|
|
||||||
*/
|
*/
|
||||||
public class BlockheadServer
|
public class BlockheadServer
|
||||||
{
|
{
|
||||||
|
public static class ServerConnection
|
||||||
|
{
|
||||||
|
|
||||||
public void start()
|
public void close()
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void flush()
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void respond(String rawstr)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSoTimeout(int ms)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upgrade() throws IOException
|
||||||
|
{
|
||||||
|
String key = "not sent";
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream()));
|
||||||
|
for (String line = in.readLine(); line != null; line = in.readLine())
|
||||||
|
{
|
||||||
|
if (line.length() == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line.startsWith("Sec-WebSocket-Key:"))
|
||||||
|
{
|
||||||
|
key = line.substring(18).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder resp = new StringBuilder();
|
||||||
|
resp.append("HTTP/1.1 101 Upgrade\r\n");
|
||||||
|
resp.append("Sec-WebSocket-Accept: ");
|
||||||
|
resp.append(AcceptHash.hashKey(key));
|
||||||
|
resp.append("\r\n");
|
||||||
|
resp.append("\r\n");
|
||||||
|
|
||||||
|
write(resp.toString().getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(byte[] bytes)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(byte[] buf, int offset, int length)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(int b)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerConnection accept()
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void accept(Socket connection) throws IOException
|
||||||
|
{
|
||||||
|
String key = "not sent";
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||||
|
for (String line = in.readLine(); line != null; line = in.readLine())
|
||||||
|
{
|
||||||
|
if (line.length() == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line.startsWith("Sec-WebSocket-Key:"))
|
||||||
|
{
|
||||||
|
key = line.substring(18).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder resp = new StringBuilder();
|
||||||
|
resp.append("HTTP/1.1 101 Upgrade\r\n");
|
||||||
|
resp.append("Sec-WebSocket-Accept: ");
|
||||||
|
resp.append(AcceptHash.hashKey(key));
|
||||||
|
resp.append("\r\n");
|
||||||
|
resp.append("\r\n");
|
||||||
|
|
||||||
|
connection.getOutputStream().write(resp.toString().getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPort()
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void respondToClient(Socket connection, String serverResponse) throws IOException
|
||||||
|
{
|
||||||
|
InputStream in = null;
|
||||||
|
InputStreamReader isr = null;
|
||||||
|
BufferedReader buf = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
in = connection.getInputStream();
|
||||||
|
isr = new InputStreamReader(in);
|
||||||
|
buf = new BufferedReader(isr);
|
||||||
|
String line;
|
||||||
|
while ((line = buf.readLine()) != null)
|
||||||
|
{
|
||||||
|
// System.err.println(line);
|
||||||
|
if (line.length() == 0)
|
||||||
|
{
|
||||||
|
// Got the "\r\n" line.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// System.out.println("[Server-Out] " + serverResponse);
|
||||||
|
out = connection.getOutputStream();
|
||||||
|
out.write(serverResponse.getBytes());
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IO.close(buf);
|
||||||
|
IO.close(isr);
|
||||||
|
IO.close(in);
|
||||||
|
IO.close(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start()
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,335 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2011 Intalio, Inc.
|
||||||
|
* ======================================================================
|
||||||
|
* 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.examples;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
import org.eclipse.jetty.websocket.api.OpCode;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketConnection;
|
||||||
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
|
import org.eclipse.jetty.websocket.client.WebSocketClientFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is not a general purpose websocket client. It's only for testing the websocket server and is hardwired to a specific draft version of the protocol.
|
||||||
|
*/
|
||||||
|
public class TestClient
|
||||||
|
{
|
||||||
|
public class TestSocket extends WebSocketAdapter
|
||||||
|
{
|
||||||
|
public void disconnect() throws Exception
|
||||||
|
{
|
||||||
|
super.getConnection().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketBinary(byte[] payload, int offset, int len)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketClose(int statusCode, String reason)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
super.onWebSocketClose(statusCode,reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketConnect(WebSocketConnection connection)
|
||||||
|
{
|
||||||
|
if (_verbose)
|
||||||
|
{
|
||||||
|
System.err.printf("%s#onWebSocketConnect %s %s\n",this.getClass().getSimpleName(),connection,connection.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(OpCode op, byte[] data, int maxFragmentLength)
|
||||||
|
{
|
||||||
|
_starts.add(System.nanoTime());
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
int len = data.length;
|
||||||
|
if ((maxFragmentLength > 0) && (len > maxFragmentLength))
|
||||||
|
{
|
||||||
|
len = maxFragmentLength;
|
||||||
|
}
|
||||||
|
__messagesSent++;
|
||||||
|
while (off < data.length)
|
||||||
|
{
|
||||||
|
__framesSent++;
|
||||||
|
byte flags = (byte)((off + len) == data.length?0x8:0);
|
||||||
|
// byte op = (byte)(off == 0?opcode:WebSocketConnectionRFC6455.OP_CONTINUATION);
|
||||||
|
|
||||||
|
if (_verbose)
|
||||||
|
{
|
||||||
|
// System.err.printf("%s#sendFrame %s|%s %s\n",
|
||||||
|
// this.getClass().getSimpleName(),
|
||||||
|
// TypeUtil.toHexString(flags),
|
||||||
|
// TypeUtil.toHexString(op),
|
||||||
|
// TypeUtil.toHexString(data,off,len));
|
||||||
|
}
|
||||||
|
|
||||||
|
// _connection.sendFrame(flags,op,data,off,len);
|
||||||
|
|
||||||
|
off += len;
|
||||||
|
if ((data.length - off) > len)
|
||||||
|
{
|
||||||
|
len = data.length - off;
|
||||||
|
}
|
||||||
|
if ((maxFragmentLength > 0) && (len > maxFragmentLength))
|
||||||
|
{
|
||||||
|
len = maxFragmentLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean _verbose = false;
|
||||||
|
|
||||||
|
private static final Random __random = new Random();
|
||||||
|
|
||||||
|
private final String _host;
|
||||||
|
private final int _port;
|
||||||
|
private final String _protocol;
|
||||||
|
private final int _timeout;
|
||||||
|
|
||||||
|
private static boolean __quiet;
|
||||||
|
private static int __framesSent;
|
||||||
|
private static int __messagesSent;
|
||||||
|
private static AtomicInteger __framesReceived = new AtomicInteger();
|
||||||
|
private static AtomicInteger __messagesReceived = new AtomicInteger();
|
||||||
|
|
||||||
|
private static AtomicLong __totalTime = new AtomicLong();
|
||||||
|
private static AtomicLong __minDuration = new AtomicLong(Long.MAX_VALUE);
|
||||||
|
private static AtomicLong __maxDuration = new AtomicLong(Long.MIN_VALUE);
|
||||||
|
private static long __start;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception
|
||||||
|
{
|
||||||
|
String host = "localhost";
|
||||||
|
int port = 8080;
|
||||||
|
String protocol = null;
|
||||||
|
int count = 10;
|
||||||
|
int size = 64;
|
||||||
|
int fragment = 4000;
|
||||||
|
boolean binary = false;
|
||||||
|
int clients = 1;
|
||||||
|
int delay = 1000;
|
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++)
|
||||||
|
{
|
||||||
|
String a = args[i];
|
||||||
|
if ("-p".equals(a) || "--port".equals(a))
|
||||||
|
{
|
||||||
|
port = Integer.parseInt(args[++i]);
|
||||||
|
}
|
||||||
|
else if ("-h".equals(a) || "--host".equals(a))
|
||||||
|
{
|
||||||
|
host = args[++i];
|
||||||
|
}
|
||||||
|
else if ("-c".equals(a) || "--count".equals(a))
|
||||||
|
{
|
||||||
|
count = Integer.parseInt(args[++i]);
|
||||||
|
}
|
||||||
|
else if ("-s".equals(a) || "--size".equals(a))
|
||||||
|
{
|
||||||
|
size = Integer.parseInt(args[++i]);
|
||||||
|
}
|
||||||
|
else if ("-f".equals(a) || "--fragment".equals(a))
|
||||||
|
{
|
||||||
|
fragment = Integer.parseInt(args[++i]);
|
||||||
|
}
|
||||||
|
else if ("-P".equals(a) || "--protocol".equals(a))
|
||||||
|
{
|
||||||
|
protocol = args[++i];
|
||||||
|
}
|
||||||
|
else if ("-v".equals(a) || "--verbose".equals(a))
|
||||||
|
{
|
||||||
|
_verbose = true;
|
||||||
|
}
|
||||||
|
else if ("-b".equals(a) || "--binary".equals(a))
|
||||||
|
{
|
||||||
|
binary = true;
|
||||||
|
}
|
||||||
|
else if ("-C".equals(a) || "--clients".equals(a))
|
||||||
|
{
|
||||||
|
clients = Integer.parseInt(args[++i]);
|
||||||
|
}
|
||||||
|
else if ("-d".equals(a) || "--delay".equals(a))
|
||||||
|
{
|
||||||
|
delay = Integer.parseInt(args[++i]);
|
||||||
|
}
|
||||||
|
else if ("-q".equals(a) || "--quiet".equals(a))
|
||||||
|
{
|
||||||
|
__quiet = true;
|
||||||
|
}
|
||||||
|
else if (a.startsWith("-"))
|
||||||
|
{
|
||||||
|
usage(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestClient[] client = new TestClient[clients];
|
||||||
|
|
||||||
|
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
__start = System.currentTimeMillis();
|
||||||
|
protocol = protocol == null?"echo":protocol;
|
||||||
|
|
||||||
|
for (int i = 0; i < clients; i++)
|
||||||
|
{
|
||||||
|
client[i] = new TestClient(factory,host,port,protocol,60000);
|
||||||
|
client[i].open();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Jetty WebSocket PING " + host + ":" + port + " (" + new InetSocketAddress(host,port) + ") " + clients + " clients " + protocol);
|
||||||
|
|
||||||
|
for (int p = 0; p < count; p++)
|
||||||
|
{
|
||||||
|
long next = System.currentTimeMillis() + delay;
|
||||||
|
|
||||||
|
OpCode op = OpCode.TEXT;
|
||||||
|
if (binary)
|
||||||
|
{
|
||||||
|
op = OpCode.BINARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte data[] = null;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case TEXT:
|
||||||
|
{
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
while (b.length() < size)
|
||||||
|
{
|
||||||
|
b.append('A' + __random.nextInt(26));
|
||||||
|
}
|
||||||
|
data = b.toString().getBytes(StringUtil.__UTF8_CHARSET);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BINARY:
|
||||||
|
{
|
||||||
|
data = new byte[size];
|
||||||
|
__random.nextBytes(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < clients; i++)
|
||||||
|
{
|
||||||
|
client[i].send(op,data,fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (System.currentTimeMillis() < next)
|
||||||
|
{
|
||||||
|
Thread.sleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
for (int i = 0; i < clients; i++)
|
||||||
|
{
|
||||||
|
if (client[i] != null)
|
||||||
|
{
|
||||||
|
client[i].disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long duration = System.currentTimeMillis() - __start;
|
||||||
|
System.out.println("--- " + host + " websocket ping statistics using " + clients + " connection" + (clients > 1?"s":"") + " ---");
|
||||||
|
System.out.printf("%d/%d frames sent/recv, %d/%d mesg sent/recv, time %dms %dm/s %.2fbps%n",__framesSent,__framesReceived.get(),__messagesSent,
|
||||||
|
__messagesReceived.get(),duration,((1000L * __messagesReceived.get()) / duration),(1000.0D * __messagesReceived.get() * 8 * size)
|
||||||
|
/ duration / 1024 / 1024);
|
||||||
|
System.out.printf("rtt min/ave/max = %.3f/%.3f/%.3f ms\n",__minDuration.get() / 1000000.0,__messagesReceived.get() == 0?0.0:(__totalTime.get()
|
||||||
|
/ __messagesReceived.get() / 1000000.0),__maxDuration.get() / 1000000.0);
|
||||||
|
|
||||||
|
factory.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage(String[] args)
|
||||||
|
{
|
||||||
|
System.err.println("ERROR: " + Arrays.asList(args));
|
||||||
|
System.err.println("USAGE: java -cp CLASSPATH " + TestClient.class + " [ OPTIONS ]");
|
||||||
|
System.err.println(" -h|--host HOST (default localhost)");
|
||||||
|
System.err.println(" -p|--port PORT (default 8080)");
|
||||||
|
System.err.println(" -b|--binary");
|
||||||
|
System.err.println(" -v|--verbose");
|
||||||
|
System.err.println(" -q|--quiet");
|
||||||
|
System.err.println(" -c|--count n (default 10)");
|
||||||
|
System.err.println(" -s|--size n (default 64)");
|
||||||
|
System.err.println(" -f|--fragment n (default 4000) ");
|
||||||
|
System.err.println(" -P|--protocol echo|echo-assemble|echo-fragment|echo-broadcast");
|
||||||
|
System.err.println(" -C|--clients n (default 1) ");
|
||||||
|
System.err.println(" -d|--delay n (default 1000ms) ");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockingQueue<Long> _starts = new LinkedBlockingQueue<Long>();
|
||||||
|
|
||||||
|
int _messageBytes;
|
||||||
|
int _frames;
|
||||||
|
byte _opcode = -1;
|
||||||
|
private final CountDownLatch _handshook = new CountDownLatch(1);
|
||||||
|
private WebSocketClientFactory factory;
|
||||||
|
private TestSocket socket;
|
||||||
|
|
||||||
|
public TestClient(WebSocketClientFactory factory, String host, int port, String protocol, int timeoutMS) throws Exception
|
||||||
|
{
|
||||||
|
this.factory = factory;
|
||||||
|
_host = host;
|
||||||
|
_port = port;
|
||||||
|
_protocol = protocol;
|
||||||
|
_timeout = timeoutMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnect()
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void open() throws Exception
|
||||||
|
{
|
||||||
|
WebSocketClient client = factory.newWebSocketClient();
|
||||||
|
client.getPolicy().setMaxIdleTime(_timeout);
|
||||||
|
client.setProtocol(_protocol);
|
||||||
|
socket = new TestSocket();
|
||||||
|
URI wsUri = new URI("ws://" + _host + ":" + _port + "/");
|
||||||
|
client.connect(wsUri,socket).get(10,TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send(OpCode op, byte[] data, int fragment)
|
||||||
|
{
|
||||||
|
socket.send(op,data,fragment);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue